Pass instance of type from argument list into function declaration (dynamic environment capture) - arguments

I made a library that implements an optimization algorithm here. These are the signatures for the relevant function and trait:
pub fn cmaes_loop<T>(object: &T, options: CMAESOptions) -> Option<Vec<f64>>
where T: 'static + FitnessFunction
pub trait FitnessFunction {
fn get_fitness(&self, parameters: &[f64]) -> f64;
}
If I apply the library to a simple problem, I can calculate the fitness based on the parameters given. However, let's say I want to optimize the shape of a wing for a given size and material:
struct Wing<'a> {
material: String,
size: i32,
parameters: &'a [f64]
}
I can implement the fitness function to read from the other fields and factor them into the calculation, then give an instance of Wing to the cmaes_loop function to optimize a specific material and size. I am making another library that will use this one. It optimizes something else (like Wing), so I have a wrapper trait to make the second library easier to use:
pub trait WingFitnessFunction {
fn get_fitness(&self, wing: &Wing) -> f64;
}
I implement the FitnessFunction trait like this:
impl FitnessFunction for Wing {
fn get_fitness(&self, parameters: &[f64]) -> f64 {
let wing = Wing {
parameters: parameters,
.. self
};
// error here
let result = WingFitnessFunction::get_fitness(wing);
}
}
WingFitnessFunction has a &self argument for the the same reason FitnessFunction does. I want a user of my second library to be able to pass an instance of their type that implements the WingFitnessFunction. I put the impl of FitnessFunction into the body of a function that takes an instance of the user's type:
fn foo<T: WingFitnessFunction>(object: T) {
impl FitnessFunction for Wing {
...
object.get_fitness(wing)
}
But when I try to call the get_fitness method of object I get this error:
can't capture dynamic environment in a fn item; use the || { ... } closure form instead
I need to know how to call object's get_fitness method on a specific instance (a closure could do this, but I do not know how I would make it work with the cmaes_loop function).

The solution to my problem is to add a wrapper:
struct WingWrapper<'a, T: WingFitnessFunction> {
wing: Wing,
object: T
}
And implement FitnessFunction for it:
impl<'a, T: WingFitnessFunction> FitnessFunction for WingWrapper<'a, T> {
fn get_fitness(&self, parameters: &[f64]) -> f64 {
let wing = Wing {
parameters: parameters,
.. self.wing
}
self.object.get_fitness(&wing)
}
}
In the function that takes a type that implements WingFitnessFunction as an argument, first build a WingWrapper:
let wrapper = WingWrapper {
wing: // Instance of wing here
object: // Instance of object here (taken as an argument)
}
Now, you can call the get_fitness method from the WingFitnessFunction trait through the method from FitnessFunction:
println!("{}", wrapper.get_fitness(&[1, 2, 3]));
This does not add any difficult to using the library; it is exactly the same from the user's perspective.

Related

How can I use the same default implementation for this Rust trait

I want to implement a trait that allows assigning generic types. So far I have tested this for u32 and String types:
trait Test {
fn test(&self, input: &str) -> Self;
}
impl Test for String {
fn test(&self, input: &str) -> Self {
input.parse().unwrap()
}
}
impl Test for u32 {
fn test(&self, input: &str) -> Self {
input.parse().unwrap()
}
}
fn main() {
let mut var = 0u32;
let mut st = String::default();
var = var.test("12345678");
st = st.test("Text");
println!("{}, {}", var, st);
}
I know this code is not perfect, and I should be using a Result return instead of unwrapping, but please set this aside as this is a quick example. The implementations for u32 and String are exactly the same, so I would like to use a default implementation for both instead of copying & pasting the code. I have tried using one, but as the returned type Self differs in both, compiler cannot determine the type size and errors.
How could I write a default implementation in this case?
Default implementation
The following bounds on Self are required for the default implementation:
Self: Sized because Self is returned from the function and will be placed in the caller's stack
Self: FromStr because you're calling parse() on input and expecting it to produce a value of type Self
<Self as FromStr>::Err: Debug because when you unwrap a potential error and the program panics Rust wants to be able to print the error message, which requires the error type to implement Debug
Full implementation:
use std::fmt::Debug;
use std::str::FromStr;
trait Test {
fn test(&self, input: &str) -> Self
where
Self: Sized + FromStr,
<Self as FromStr>::Err: Debug,
{
input.parse().unwrap()
}
}
impl Test for String {}
impl Test for u32 {}
fn main() {
let mut var = 0u32;
let mut st = String::default();
var = var.test("12345678");
st = st.test("Text");
println!("{}, {}", var, st);
}
playground
Generic implementation
A generic blanket implementation is also possible, where you automatically provide an implementation of Test for all types which satisfy the trait bounds:
use std::fmt::Debug;
use std::str::FromStr;
trait Test {
fn test(&self, input: &str) -> Self;
}
impl<T> Test for T
where
T: Sized + FromStr,
<T as FromStr>::Err: Debug,
{
fn test(&self, input: &str) -> Self {
input.parse().unwrap()
}
}
fn main() {
let mut var = 0u32;
let mut st = String::default();
var = var.test("12345678");
st = st.test("Text");
println!("{}, {}", var, st);
}
playground
Macro implementation
This implementation, similar to the default implementation, allows you to pick which types get the implementation, but it's also similar to the generic implementation, in that it doesn't require you to modify the trait method signature with any additional trait bounds:
trait Test {
fn test(&self, input: &str) -> Self;
}
macro_rules! impl_Test_for {
($t:ty) => {
impl Test for $t {
fn test(&self, input: &str) -> Self {
input.parse().unwrap()
}
}
}
}
impl_Test_for!(u32);
impl_Test_for!(String);
fn main() {
let mut var = 0u32;
let mut st = String::default();
var = var.test("12345678");
st = st.test("Text");
println!("{}, {}", var, st);
}
playground
Key differences
The key differences between the 3 approaches:
The default implementation makes the trait bounds inherent to the method's signature, so all types which impl Test must be sized, and have a FromStr impl with a debuggable error type.
The default implementation allows you to selectively pick which types get Test implementations.
The generic implementation doesn't add any trait bounds to the trait method's signature so a greater variety of types could potentially implement the trait.
The generic implementation automatically implements the trait for all types which satisfy the bounds, you cannot selectively "opt out" of the generic implementation if there are some types which you'd prefer not to implement the trait.
The macro implementation does not require modifying the trait method signature with additional trait bounds and allows you to selectively pick which types get the implementation.
The macro implementation is a macro and suffers all the downsides of being a macro: harder to read, write, maintain, increases compile times, and macros are essentially opaque to static code analyzers which makes it harder to easily type-check your code.

Rust - wrapping trait in an enum for memory layout and simpler generics?

So I had something like the following in my code:
// Trait
trait Shape {
fn area(&self) -> f32;
}
// Rect
struct Rect {
width: f32,
height: f32
}
impl Shape for Rect {
fn area(&self) -> f32 {
self.width * self.height
}
}
// Circle
struct Circle {
radius: f32
}
impl Shape for Circle {
fn area(&self) -> f32 {
self.radius * self.radius * std::f32::consts::PI
}
}
// usage
fn use_shapes(shapes: Vec<Box<dyn Shape>>) {
// ...
}
And I really didn't like the Box<dyn ...>, both for performance and because it felt gross. My implementations of my trait were few and explicit, so it felt like a good candidate to become an enum.
In the process of converting it to an enum, I stumbled upon the following pattern:
// Wrapper enum
enum ShapeEnum {
Rect(Rect),
Circle(Circle)
}
impl Shape for ShapeEnum {
fn area(&self) -> f32 {
match self {
ShapeEnum::Rect(data) => data.area(),
ShapeEnum::Circle(data) => data.area(),
}
}
}
// new usage
fn use_shapes(shapes: Vec<ShapeEnum>) {
// ...
}
It's pretty neat. It also feels like cheating somehow. It compiles and works as expected, it's just unusual enough that I wanted to see if there are any unexpected drawbacks/costs/quirks that I'm not seeing right now?
I'm also wondering if, because of the deterministic nature of the enum implementation, it would make a good macro? Automatically generating an enum around a trait and a set of its implementors, which itself implements the trait just like a dyn version would.
I wanted to see if there are any unexpected drawbacks/costs/quirks that I'm not seeing right now?
The only real disadvantage I can think of is that you end up centralizing the definition of all those types - making it harder to allow third parties to hook into your code.
You can get around this by adding a dynamic dispatch enum, which means you only get slower behaviour on those externally defined types.
// Wrapper enum
enum ShapeEnum {
Rect(Rect),
Circle(Circle),
Dynamic(Box<T: Shape>),
}
impl Shape for ShapeEnum {
fn area(&self) -> f32 {
match self {
ShapeEnum::Rect(data) => data.area(),
ShapeEnum::Circle(data) => data.area(),
ShapeEnum::Dynamic::(data) => data.area(),
}
}
}
I'm also wondering if, because of the deterministic nature of the enum
implementation, it would make a good macro? Automatically generating
an enum around a trait and a set of its implementors, which itself
implements the trait just like a dyn version would
It looks like the enum_dispatch crate does almost exactly that.
Disclaimer: I've not used it myself.

Replace a struct member with a new value that uses the previous value

I have a struct which owns a boxed value of some trait type. The struct itself also implements the same trait. I would like to replace the value with a new instance of the same struct, which wraps it.
The following code, which does not compile, should make it more clear what I am trying to do:
trait T {}
struct S {
t: Box<dyn T>,
}
impl T for S {}
impl S {
fn new(t: Box<dyn T>) -> Self {
Self { t }
}
fn wrap_t(&mut self) {
self.t = Box::new(Self::new(self.t))
}
}
This fails:
error[E0507]: cannot move out of borrowed content
--> src/lib.rs:14:37
|
14 | self.t = Box::new(Self::new(self.t))
| ^^^^ cannot move out of borrowed content
Implementing wrap_t like this does compile:
use std::mem;
fn wrap_t(&mut self) {
unsafe {
let old_t = mem::replace(&mut self.t, mem::uninitialized());
let new_t = Box::new(Self::new(old_t));
let uninit = mem::replace(&mut self.t, new_t);
mem::forget(uninit);
}
}
I wonder if there is a safe way to do this.
The only unsafe function you are using is mem::uninitialized. You need something to pass to mem::replace, but implementing Default won't work because default() returns Self, which prevents it from being object-safe. Similarly, you can't implement Clone to duplicate the old value, since clone() also returns Self.
You can just implement a dummy type for the purpose though:
struct Dummy;
impl T for Dummy {}
fn wrap_t(&mut self) {
let old_t = mem::replace(&mut self.t, Box::new(Dummy));
let new_t = Box::new(Self::new(old_t));
mem::replace(&mut self.t, new_t);
}
You also won't need the mem::forget here now either (I'm assuming that was there to prevent undefined behaviour when the uninitialised memory was dropped).
As an alternative to Clone, you can roll your own own, which clones to a Box<dyn T>, avoiding having a Self in the method signature, so the trait stays object safe:
trait T: Debug {
fn clone_in_box(&self) -> Box<dyn T>;
}
impl T for S {
fn clone_in_box(&self) -> Box<dyn T> {
Box::new(S {
t: self.t.clone_in_box(),
})
}
}
fn wrap_t(&mut self) {
let cloned = self.clone_in_box();
let old_t = mem::replace(&mut self.t, cloned);
let new_t = Box::new(Self::new(old_t));
mem::replace(&mut self.t, new_t);
}
There is also an alternative design, which is much simpler to understand when reading the code. That is just to consume self and return a new object:
fn wrap_t(self) -> Self {
Self::new(Box::new(Self::new(self.t)))
}
And instead of this:
s.wrap_t();
You would do:
s = s.wrap_t();

"error: trait bounds are not allowed in structure definitions" when attempting to use polymorphism

Editor's note: This question was asked before Rust 1.0 and before certain features were implemented. The code as-is works today.
I'm writing a board game AI in Rust. There are multiple rulesets for the game and I'd like to have the rules logic separated from the board layout (they are currently mixed). In a language like Ruby, I'd have the separate rule sets implement the same interface. In Rust I thought about using a trait and parameterizing the Board with the ruleset I want to use (e.g. Board<Rules1>::new()).
Saving an object that implements this trait in a struct (like Board) is not allowed. I could turn the Rules into an enum, but it looks a bit messy to me because I can't define separate implementations for the members of the enum. Using pattern matching would work, but that splits the functionality along the function axis and not along the struct axis. Is this just something I have to live with or there some way?
The following code is what I'd like to use:
pub struct Rules1;
pub struct Rules2;
trait Rules {
fn move_allowed() -> bool;
}
impl Rules for Rules1 {
fn move_allowed() -> bool {
true
}
}
impl Rules for Rules2 {
fn move_allowed() -> bool {
false
}
}
struct Board<R: Rules> {
rules: R
}
fn main() {}
It produces the following error:
test.rs:20:1: 22:2 error: trait bounds are not allowed in structure definitions
test.rs:20 struct Board<R: Rules> {
test.rs:21 rules: R
test.rs:22 }
error: aborting due to previous error
The code presented in the question works on all recent versions of Rust, trait bounds on structs are now allowed. The original answer is also still valid.
You need to refine that in the trait implementation, not the struct definition.
pub struct Rules1;
pub struct Rules2;
trait Rules {
fn move_allowed(&self) -> bool;
}
impl Rules for Rules1 {
fn move_allowed(&self) -> bool {
true
}
}
impl Rules for Rules2 {
fn move_allowed(&self) -> bool {
false
}
}
struct Board<R> {
rules: R,
}
impl<R: Rules> Board<R> {
fn move_allowed(&self) -> bool {
self.rules.move_allowed()
}
}
fn main() {
let board = Board { rules: Rules2 };
assert!(!board.move_allowed());
}

How to specify a lifetime for an Option<closure>?

I'm trying to put a field on a struct that should hold an Option<closure>.
However, Rust is yelling at me that I have to specify the lifetime (not that I would have really grokked that yet). I'm trying my best to do so but Rust is never happy with what I come up with. Take a look at my inline comments for the compile errors I got.
struct Floor{
handler: Option<|| ->&str> //this gives: missing lifetime specifier
//handler: Option<||: 'a> // this gives: use of undeclared lifetime name `'a`
}
impl Floor {
// I guess I need to specify life time here as well
// but I can't figure out for the life of me what's the correct syntax
fn get(&mut self, handler: || -> &str){
self.handler = Some(handler);
}
}
This gets a bit trickier.
As a general rule of thumb, whenever you're storing a borrowed reference (i.e., an & type) in a data structure, then you need to name its lifetime. In this case, you were on the right track by using a 'a, but that 'a has to be introduced in the current scope. It's done the same way you introduce type variables. So to define your Floor struct:
struct Floor<'a> {
handler: Option<|| -> &'a str>
}
But there's another problem here. The closure itself is also a reference with a lifetime, which also must be named. So there are two different lifetimes at play here! Try this:
struct Floor<'cl, 'a> {
handler: Option<||:'cl -> &'a str>
}
For your impl Floor, you also need to introduce these lifetimes into scope:
impl<'cl, 'a> Floor<'cl, 'a> {
fn get(&mut self, handler: ||:'cl -> &'a str){
self.handler = Some(handler);
}
}
You could technically reduce this down to one lifetime and use ||:'a -> &'a str, but this implies that the &str returned always has the same lifetime as the closure itself, which I think is a bad assumption to make.
Answer for current Rust version 1.x:
There are two possibilities to get what you want: either an unboxed closure or a boxed one. Unboxed closures are incredibly fast (most of the time, they are inlined), but they add a type parameter to the struct. Boxed closures add a bit freedom here: their type is erased by one level of indirection, which sadly is a bit slower.
My code has some example functions and for that reason it's a bit longer, please excuse that ;)
Unboxed Closure
Full code:
struct Floor<F>
where F: for<'a> FnMut() -> &'a str
{
handler: Option<F>,
}
impl<F> Floor<F>
where F: for<'a> FnMut() -> &'a str
{
pub fn with_handler(handler: F) -> Self {
Floor {
handler: Some(handler),
}
}
pub fn empty() -> Self {
Floor {
handler: None,
}
}
pub fn set_handler(&mut self, handler: F) {
self.handler = Some(handler);
}
pub fn do_it(&mut self) {
if let Some(ref mut h) = self.handler {
println!("Output: {}", h());
}
}
}
fn main() {
let mut a = Floor::with_handler(|| "hi");
a.do_it();
let mut b = Floor::empty();
b.set_handler(|| "cheesecake");
b.do_it();
}
Now this has some typical problems: You can't simply have a Vec of multiple Floors and every function using a Floor object needs to have type parameter on it's own. Also: if you remove the line b.set_handler(|| "cheesecake");, the code won't compile, because the compiler is lacking type information for b.
In some cases you won't run into those problems -- in others you'll need another solution.
Boxed closures
Full code:
type HandlerFun = Box<for<'a> FnMut() -> &'a str>;
struct Floor {
handler: Option<HandlerFun>,
}
impl Floor {
pub fn with_handler(handler: HandlerFun) -> Self {
Floor {
handler: Some(handler),
}
}
pub fn empty() -> Self {
Floor {
handler: None,
}
}
pub fn set_handler(&mut self, handler: HandlerFun) {
self.handler = Some(handler);
}
pub fn do_it(&mut self) {
if let Some(ref mut h) = self.handler {
println!("Output: {}", h());
}
}
}
fn main() {
let mut a = Floor::with_handler(Box::new(|| "hi"));
a.do_it();
let mut b = Floor::empty();
b.set_handler(Box::new(|| "cheesecake"));
b.do_it();
}
It's a bit slower, because we have a heap allocation for every closure and when calling a boxed closure it's an indirect call most of the time (CPUs don't like indirect calls...).
But the Floor struct does not have a type parameter, so you can have a Vec of them. You can also remove b.set_handler(Box::new(|| "cheesecake")); and it will still work.

Resources