To learn the Rust language, I'm taking an old C++ library I had lying around and trying to convert it to Rust. It used a lot of C++11 closures and I'm having some difficulty getting the concepts to translate.
In C++ I had something like this:
// library.h
struct Event {
// just some data
};
class Object {
public:
// ...
std::function<void(Event&)>& makeFunc(std::string& s) {
return m_funcs[s];
}
// ...
private:
// ...
std::map<std::string, std::function<void(Event&)>> m_funcs;
// ...
};
// main.cpp using the library
int main()
{
Object foo;
foo.makeFunc("func1") = [&]{
// do stuff
};
return 0;
}
The part that I'm having trouble with is properly storing the functions in a Rust HashMap collection. I tried this:
struct Event;
struct Object {
m_funcs : HashMap<String, FnMut(&Event)>
}
impl Object {
// send f as another parameter rather than try and return borrow
// compiler was complaining
fn makeFunc(&mut self, s : &str,f: FnMut(&Event)) {
self.m_funcs.insert(String::from_str(s), f);
}
}
but it says the trait core::marker::Sized is not implemented for the type 'for('r) core::ops::FnMut(&'r CreateEvent)'
This makes sense because FnMut is a trait, and therefore has no known size for the HashMap to make at compile time. So I figure that the hashmap would require an actual pointer rather than an abstract type. So I change it to this
struct Object {
m_funcs : HashMap<String, Box<FnMut(&Event)>>
}
impl Object {
fn makeFunc(&mut self, s : &str, f: &FnMut(&Event)) {
self.m_funcs.insert(String::from_str(s), Box::new(f));
}
}
now it says the trait 'for('r) core::ops::Fn<(&'r CreateEvent,)>' is not implemented for the type '&for('r) core::ops::FnMut(&'r CreateEvent)' [E0277] at the insert. This error makes no sense to me at all. Can someone explain to me the proper way to store a reference to a non-escaping closure in a HashMap?
You have taken a &FnMut(&Event)—a trait object—and, after boxing it, wish to store it as a Box<FnMut(&Event)>. Thus, you require that &FnMut(&Event) must implement FnMut(&Event), which it does not (and clearly cannot, for FnMut.call_mut takes &mut self).
What you wanted was to take an arbitrary type that implements FnMut(&Event)—that is, use generics—and take it by value. The signature is thus this:
fn make_func<F: FnMut(&Event)>(&mut self, s: &str, f: F)
It gets a little more complex than this due to lifetimes, however, but what you wish to do with regards to that may vary; Storing an unboxed closure with a reference arg in a HashMap has more information on that topic. Here’s what I believe you’re most likely to want:
struct Object<'a> {
m_funcs: HashMap<String, Box<FnMut(&Event) + 'a>>,
}
impl<'a> Object<'a> {
fn make_func<F: FnMut(&Event) + 'a>(&mut self, s: &str, f: F) {
self.m_funcs.insert(String::from_str(s), Box::new(f));
}
}
You could remove all the 'a in favour of just a single + 'static bound on F if you are happy to not let any of the closures capture references to their environments.
Related
I have a public trait, Parser, that defines an external interface. I then have a private ParserImpl struct that implements the methods (actually, I have several implementations, which is the idea behind using the trait to abstract away).
use std::io;
pub trait Parser {
// ...omitted
}
struct ParserImpl<R: io::Read> {
// ...omitted
stream: R,
}
impl<R: io::Read> ParserImpl<R> {
// ...methods
fn new(stream: R) -> ParserImpl<R> {
ParserImpl {
// ...omitted
stream: stream,
}
}
}
impl<R: io::Read> Parser for ParserImpl<R> {
// ...methods
}
To create a parser instance, I use a function to hide ParserImpl.
pub fn make_parser<'a, R>(stream: R) -> Box<Parser + 'a>
where
R: io::Read + 'a,
{
Box::new(ParserImpl::new(stream))
}
This is all well and good... and it works... but the make_parser function troubles me. I feel that there must be a simpler way to approach this and like I'm missing something important, as this seems like a potential pitfall whenever using a trait like io::Read to abstract away the source of data.
I understand the need to specify lifetimes (Parameter type may not live long enough?) but I am a bit stumped on whether I can have both a clean and simple interface, and also use a trait like io::Read.
Is there a "cleaner," or perhaps more idiomatic way, to use traits like io::Read that I am missing? If not, that's okay, but I'm pretty new to Rust and when I wrote the above function I kept thinking "this can't be right..."
To make this sample runnable, here's a main:
fn main() {
use std::fs;
let file: fs::File = fs::File::open("blabby.txt").unwrap();
let parser = make_parser(file);
}
That is the idiomatic way of writing the code that has that meaning, but you may not want that meaning.
For example, if you don't need to create a boxed trait object, you can just return the parameterized value directly, or in this case just use the result of ParserImpl::new. This is my default form until I know I need dynamic dispatch provided by some trait object.
You could also require the 'static lifetime instead of introducing a new lifetime 'a, but this reduces the range of allowed types that you can pass into make_parser:
pub fn make_parser<R>(stream: R) -> Box<Parser>
where
R: io::Read + 'static,
{
Box::new(ParserImpl::new(stream))
}
I'm new to Rust and have seen some examples of people using Box to allow pushing many types that implement a certain Trait onto a Vec. When using a Trait with Generics, I have run into an issue.
error[E0038]: the trait `collision::collision_detection::Collidable` cannot be made into an object
--> src/collision/collision_detection.rs:19:5
|
19 | collidables: Vec<Box<Collidable<P, M>>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `collision::collision_detection::Collidable` cannot be made into an object
|
= note: method `get_ncollide_shape` has generic type parameters
error: aborting due to previous error
error: Could not compile `game_proto`.
To learn more, run the command again with --verbose.
Here is my code
extern crate ncollide;
extern crate nalgebra as na;
use self::ncollide::shape::Shape;
use self::ncollide::math::Point;
use self::ncollide::math::Isometry;
use self::na::Isometry2;
pub trait Collidable<P: Point, M> {
fn get_ncollide_shape<T: Shape<P, M>>(&self) -> Box<T>;
fn get_isometry(&self) -> Isometry2<f64>;
}
pub struct CollisionRegistry<P, M>
where
P: Point,
M: Isometry<P>,
{
collidables: Vec<Box<Collidable<P, M>>>,
}
impl<P: Point, M: Isometry<P>> CollisionRegistry<P, M> {
pub fn new() -> Self {
let objs: Vec<Box<Collidable<P, M>>> = Vec::new();
CollisionRegistry { collidables: objs }
}
pub fn register<D>(&mut self, obj: Box<D>)
where
D: Collidable<P, M>,
{
self.collidables.push(obj);
}
}
I'm trying to use collidables as a list of heterogenous game objects that will give me ncollide compatible Shapes back to feed into the collision detection engine.
EDIT:
To clear up some confusion. I'm not trying to construct and return an instance of a Trait. I'm just trying to create a Vec that will allow any instance of the Collidable trait to be pushed onto it.
Rust is a compiled language, so when it compiles your code, it needs to know all of the information it might need to generate machine code.
When you say
trait MyTrait {
fn do_thing() -> Box<u32>;
}
struct Foo {
field: Box<MyTrait>
}
you are telling Rust that Foo will contain a box containing anything implementing MyTrait. By boxing the type, the compiler will erase any additional data about the data type that isn't covered by the trait. These trait objects are implemented as a set of data fields and a table of functions (called a vtable) that contains the functions exposed by the trait, so they can be called.
When you change
fn do_thing() -> Box<u32>;
to
fn do_thing<T>() -> Box<T>;
it may look similar, but the behavior is much different. Let's take a normal function example
fn do_thing<T>(val: T) { }
fn main() {
do_thing(true);
do_thing(45 as u32);
}
the compiler performs what is a called monomorphization, which means your code in the compiler becomes essentially
fn do_thing_bool(val: bool) { }
fn do_thing_num(val: u32) { }
fn main() {
do_thing_bool(true);
do_thing_num(45 as u32);
}
The key thing to realize is that you are asking it to do the same thing for your trait. The problem is that the compiler can't do it. The example above relies on knowing ahead of time that do_thing is called with a number in one case and a boolean in another, and it can know with 100% certainty that those are the only two ways the function is used.
With your code
trait MyTrait {
fn do_thing<T>() -> Box<T>;
}
the compiler does not know what types do_thing will be called with, so it has no way to generate functions you'd need to call. To do that, wherever you convert the struct implementing Collidable into a boxed object it would have to know every possible return type get_ncollide_shape could have, and that is not supported.
Other links for this:
Understanding Traits and Object Safety
https://www.reddit.com/r/rust/comments/3an132/how_to_wrap_a_trait_object_that_has_generic/
How do I get over something like this:
struct Test {
foo: Option<fn()>
}
impl Test {
fn new(&mut self) {
self.foo = Option::Some(self.a);
}
fn a(&self) { /* can use Test */ }
}
I get this error:
error: attempted to take value of method `a` on type `&mut Test`
--> src/main.rs:7:36
|
7 | self.foo = Option::Some(self.a);
| ^
|
= help: maybe a `()` to call it is missing? If not, try an anonymous function
How do I pass a function pointer from a trait? Similar to what would happen in this case:
impl Test {
fn new(&mut self) {
self.foo = Option::Some(a);
}
}
fn a() { /* can't use Test */ }
What you're trying to do here is get a function pointer from a (to use Python terminology here, since Rust doesn't have a word for this) bound method. You can't.
Firstly, because Rust doesn't have a concept of "bound" methods; that is, you can't refer to a method with the invocant (the thing on the left of the .) already bound in place. If you want to construct a callable which approximates this, you'd use a closure; i.e. || self.a().
However, this still wouldn't work because closures aren't function pointers. There is no "base type" for callable things like in some other languages. Function pointers are a single, specific kind of callable; closures are completely different. Instead, there are traits which (when implemented) make a type callable. They are Fn, FnMut, and FnOnce. Because they are traits, you can't use them as types, and must instead use them from behind some layer of indirection, such as Box<FnOnce()> or &mut FnMut(i32) -> String.
Now, you could change Test to store an Option<Box<Fn()>> instead, but that still wouldn't help. That's because of the other, other problem: you're trying to store a reference to the struct inside of itself. This is not going to work well. If you manage to do this, you effectively render the Test value permanently unusable. More likely is that the compiler just won't let you get that far.
Aside: you can do it, but not without resorting to reference counting and dynamic borrow checking, which is out of scope here.
So the answer to your question as-asked is: you don't.
Let's change the question: instead of trying to crowbar a self-referential closure in, we can instead store a callable that doesn't attempt to capture the invocant at all.
struct Test {
foo: Option<Box<Fn(&Test)>>,
}
impl Test {
fn new() -> Test {
Test {
foo: Option::Some(Box::new(Self::a)),
}
}
fn a(&self) { /* can use Test */ }
fn invoke(&self) {
if let Some(f) = self.foo.as_ref() {
f(self);
}
}
}
fn main() {
let t = Test::new();
t.invoke();
}
The callable being stored is now a function that takes the invocant explicitly, side-stepping the issues with cyclic references. We can use this to store Test::a directly, by referring to it as a free function. Also note that because Test is the implementation type, I can also refer to it as Self.
Aside: I've also corrected your Test::new function. Rust doesn't have constructors, just functions that return values like any other.
If you're confident you will never want to store a closure in foo, you can replace Box<Fn(&Test)> with fn(&Test) instead. This limits you to function pointers, but avoids the extra allocation.
If you haven't already, I strongly urge you to read the Rust Book.
There are few mistakes with your code. new function (by the convention) should not take self reference, since it is expected to create Self type.
But the real issue is, Test::foo expecting a function type fn(), but Test::a's type is fn(&Test) == fn a(&self) if you change the type of foo to fn(&Test) it will work. Also you need to use function name with the trait name instead of self. Instead of assigning to self.a you should assign Test::a.
Here is the working version:
extern crate chrono;
struct Test {
foo: Option<fn(&Test)>
}
impl Test {
fn new() -> Test {
Test {
foo: Some(Test::a)
}
}
fn a(&self) {
println!("a run!");
}
}
fn main() {
let test = Test::new();
test.foo.unwrap()(&test);
}
Also if you gonna assign a field in new() function, and the value must always set, then there is no need to use Option instead it can be like that:
extern crate chrono;
struct Test {
foo: fn(&Test)
}
impl Test {
fn new() -> Test {
Test {
foo: Test::a
}
}
fn a(&self) {
println!("a run!");
}
}
fn main() {
let test = Test::new();
(test.foo)(&test); // Make sure the paranthesis are there
}
When writing callbacks for generic interfaces, it can be useful for them to define their own local data which they are responsible for creating and accessing.
In C I would just use a void pointer, C-like example:
struct SomeTool {
int type;
void *custom_data;
};
void invoke(SomeTool *tool) {
StructOnlyForThisTool *data = malloc(sizeof(*data));
/* ... fill in the data ... */
tool.custom_data = custom_data;
}
void execute(SomeTool *tool) {
StructOnlyForThisTool *data = tool.custom_data;
if (data.foo_bar) { /* do something */ }
}
When writing something similar in Rust, replacing void * with Option<Box<Any>>, however I'm finding that accessing the data is unreasonably verbose, eg:
struct SomeTool {
type: i32,
custom_data: Option<Box<Any>>,
};
fn invoke(tool: &mut SomeTool) {
let data = StructOnlyForThisTool { /* my custom data */ }
/* ... fill in the data ... */
tool.custom_data = Some(Box::new(custom_data));
}
fn execute(tool: &mut SomeTool) {
let data = tool.custom_data.as_ref().unwrap().downcast_ref::<StructOnlyForThisTool>().unwrap();
if data.foo_bar { /* do something */ }
}
There is one line here which I'd like to be able to write in a more compact way:
tool.custom_data.as_ref().unwrap().downcast_ref::<StructOnlyForThisTool>().unwrap()
tool.custom_data.as_ref().unwrap().downcast_mut::<StructOnlyForThisTool>().unwrap()
While each method makes sense on its own, in practice it's not something I'd want to write throughout a code-base, and not something I'm going to want to type out often or remember easily.
By convention, the uses of unwrap here aren't dangerous because:
While only some tools define custom data, the ones that do always define it.
When the data is set, by convention the tool only ever sets its own data. So there is no chance of having the wrong data.
Any time these conventions aren't followed, its a bug and should panic.
Given these conventions, and assuming accessing custom-data from a tool is something that's done often - what would be a good way to simplify this expression?
Some possible options:
Remove the Option, just use Box<Any> with Box::new(()) representing None so access can be simplified a little.
Use a macro or function to hide verbosity - passing in the Option<Box<Any>>: will work of course, but prefer not - would use as a last resort.
Add a trait to Option<Box<Any>> which exposes a method such as tool.custom_data.unwrap_box::<StructOnlyForThisTool>() with matching unwrap_box_mut.
Update 1): since asking this question a point I didn't include seems relevant.
There may be multiple callback functions like execute which must all be able to access the custom_data. At the time I didn't think this was important to point out.
Update 2): Wrapping this in a function which takes tool isn't practical, since the borrow checker then prevents further access to members of tool until the cast variable goes out of scope, I found the only reliable way to do this was to write a macro.
If the implementation really only has a single method with a name like execute, that is a strong indication to consider using a closure to capture the implementation data. SomeTool can incorporate an arbitrary callable in a type-erased manner using a boxed FnMut, as shown in this answer. execute() then boils down to invoking the closure stored in the struct field implementation closure using (self.impl_)(). For a more general approach, that will also work when you have more methods on the implementation, read on.
An idiomatic and type-safe equivalent of the type+dataptr C pattern is to store the implementation type and pointer to data together as a trait object. The SomeTool struct can contain a single field, a boxed SomeToolImpl trait object, where the trait specifies tool-specific methods such as execute. This has the following characteristics:
You no longer need an explicit type field because the run-time type information is incorporated in the trait object.
Each tool's implementation of the trait methods can access its own data in a type-safe manner without casts or unwraps. This is because the trait object's vtable automatically invokes the correct function for the correct trait implementation, and it is a compile-time error to try to invoke a different one.
The "fat pointer" representation of the trait object has the same performance characteristics as the type+dataptr pair - for example, the size of SomeTool will be two pointers, and accessing the implementation data will still involve a single pointer dereference.
Here is an example implementation:
struct SomeTool {
impl_: Box<SomeToolImpl>,
}
impl SomeTool {
fn execute(&mut self) {
self.impl_.execute();
}
}
trait SomeToolImpl {
fn execute(&mut self);
}
struct SpecificTool1 {
foo_bar: bool
}
impl SpecificTool1 {
pub fn new(foo_bar: bool) -> SomeTool {
let my_data = SpecificTool1 { foo_bar: foo_bar };
SomeTool { impl_: Box::new(my_data) }
}
}
impl SomeToolImpl for SpecificTool1 {
fn execute(&mut self) {
println!("I am {}", self.foo_bar);
}
}
struct SpecificTool2 {
num: u64
}
impl SpecificTool2 {
pub fn new(num: u64) -> SomeTool {
let my_data = SpecificTool2 { num: num };
SomeTool { impl_: Box::new(my_data) }
}
}
impl SomeToolImpl for SpecificTool2 {
fn execute(&mut self) {
println!("I am {}", self.num);
}
}
pub fn main() {
let mut tool1: SomeTool = SpecificTool1::new(true);
let mut tool2: SomeTool = SpecificTool2::new(42);
tool1.execute();
tool2.execute();
}
Note that, in this design, it doesn't make sense to make implementation an Option because we always associate the tool type with the implementation. While it is perfectly valid to have an implementation without data, it must always have a type associated with it.
Trying to get dynamic dispatch working in a trait static method but get a type must be known error.
I'm trying to achieve something like
F#
https://github.com/Thorium/SimpleCQRS-FSharp/blob/master/CommandSide/Domain.fs
C#
https://github.com/gregoryyoung/m-r/blob/master/SimpleCQRS/Domain.cs..
Is the only way to make the trait generic?
pub struct Aggregate<T: AggregateRoot>
{
pub id: Uuid,
agg: T,
changes: Vec<Box<Any>>
}
impl <T :AggregateRoot > Aggregate<T>
{
fn GetUncomittedChanges(&self) -> Vec<Box<Any>> { self.changes}
fn MarkChangesAsCommitted(&self) { self.changes.drain(..);}
}
trait AggregateRoot
{
fn new2() -> Self; //should be private
fn new(id: Uuid) -> Self;
fn LoadsFromHistory(changes : Vec<Box<Any>> ) -> Self
where Self: Sized
{
let newAgg = AggregateRoot::new2 ();
changes.iter().map( |e| newAgg.Apply(e) );
newAgg.MarkChangesAsCommitted();
newAgg
}
fn Apply<U: Any>(&self, arg: U) ;
fn GetId(&self) -> Uuid;
}
currently trying but gives 2 params expected 1 supplied.
Let's start with issues in how you asked the question, in the hopes that you will be able to ask better questions in the future. The complete error you are getting is:
<anon>:27:37: 27:52 error: the type of this value must be known in this context
<anon>:27 changes.iter().map( |e| newAgg.Apply(e) );
^~~~~~~~~~~~~~~
Note that the compiler error message shows you exactly which bit of code is at fault. It's useful to include that error when asking a question.
You've also included extraneous detail. For example, GetUncomittedChanges, id and GetId are all unused in your example. When solving a problem, you should produce an MCVE. This helps you understand the problem better and also allows people helping you to look at less code which usually results in faster turnaround.
Your code has a number of problems, but let's start at the first error:
let newAgg = AggregateRoot::new2 ();
This says "for any possible AggregateRoot, create a new one". Many concrete types can implement a trait (which is the point of traits), but the compiler needs to know how much space to allocate for a given instance. There might be a struct that takes 1 byte or 200 bytes; how much space needs to be allocated on the stack in this case?
To progress, you can use Self::new2 instead. That means to create a new instance of the current implementor.
The next error is
<anon>:20:16: 20:40 error: no method named `MarkChangesAsCommitted` found for type `Self` in the current scope
<anon>:20 newAgg.MarkChangesAsCommitted();
^~~~~~~~~~~~~~~~~~~~~~~~
You are calling a method on a concrete type from a trait implementation; this simply doesn't make any sense. What would happen if a bool implements this trait? It doesn't have a MarkChangesAsCommitted method. I don't know what you intended in this case, so I'll just delete it.
Now you get this error:
<anon>:19:9: 19:16 error: `changes` does not live long enough
<anon>:19 changes.iter().map( |e| newAgg.Apply(e) );
^~~~~~~
note: reference must be valid for the static lifetime...
<anon>:17:5: 21:6 note: ...but borrowed value is only valid for the scope of parameters for function at 17:4
That's because your method Apply expects to be given a type that implements Any. However, you are passing a &Box<Any>. Any has a lifetime bound of 'static, and that reference is not static. A straightforward change is to accept a reference to a type that implements Any:
fn Apply<U: Any>(&self, arg: &U);
Now that the code compiles, there's a number of stylistic issues to fix:
no space before :
no space after >
no space before (
no space inside ()
map should not be used for side effects
function and variable names are camel_case
most of the time, accept a &[T] instead of a Vec<T> as a function argument.
use "Egyptian" braces, except when you are using a where clause.
All together, your code looks like:
use std::any::Any;
struct Aggregate<T: AggregateRoot> {
agg: T,
changes: Vec<Box<Any>>
}
impl<T: AggregateRoot> Aggregate<T> {
fn mark_changes_as_committed(&self) { }
}
trait AggregateRoot {
fn new() -> Self;
fn load_from_history(changes: &[Box<Any>]) -> Self
where Self: Sized
{
let new_agg = Self::new();
for change in changes { new_agg.apply(change) }
new_agg
}
fn apply<U: Any>(&self, arg: &U);
}
fn main() {}
Is there a way to constrain the concrete types of the AggregateRoot to Aggregates so mark_changes can be called?
Not that I'm aware of. It sounds like you want to move mark_changes to the trait and force all implementors of the trait to implement it:
trait AggregateRoot {
fn load_from_history(changes: &[Box<Any>]) -> Self
where Self: Sized
{
let new_agg = Self::new();
for change in changes { new_agg.apply(change) }
new_agg.mark_changes_as_committed();
new_agg
}
fn mark_changes_as_committed(&self);
// ...
}