Rust narrow traits in function parameter - rust

I've got a situation in which I need a trait which extends another trait as a parameter in a function. I say extends because in TypeScript, this would be perfectly acceptable - an object may have more than the required list of properties.
My code looks like this: (For context, I'm attempting to implement a very simple UI toolkit)
pub trait Widget {
// stuff related to widgets
}
// All `MeasurableWidget`s are `Widget`s
pub trait MeasurableWidget: Widget {
// allow the widget to be measured
}
pub struct ScrollBox {
// stuff related to scrollboxes
}
pub trait Layout {
fn insert_child(&mut self, child: Rc<dyn Widget>);
// more stuff related to layouts
}
The issue is that a ScrollBox needs to know the dimensions of it's child, hence it's child needs to implement MeasurableWidget, rather than just Widget.
impl Layout for ScrollBox {
// Rightfully won't compile because the types don't match
fn insert_child(&mut self, child: Rc<dyn MeasurableWidget>) {
// insert magic here
}
}
Specifically, I'm getting Compiler-Error E0053:
error[E0053]: method `insert_child` has an incompatible type for trait
If this were TypeScript-land, this wouldn't be an issue. Since all MeasurableWidgets extend Widget, this is fine. However, naturally, Widget and MeasurableWidget are different sizes, so Rust can't know at compile-time which is which.
How would I enforce all calls to ScrollBox::insert_child to take an Rc<dyn MeasurableWidget> instead of allowing a plain ol' Widget, given that I need to know the dimensions of the child, and that some layouts don't require their child's dimensions?
Of course I have tried numerous variations of how the MeasurableWidget trait is defined and how the function works, but it's notoriously difficult to trick the Rust compiler into breaking certain rules. :P
Thanks
Jake

You could solve it with an associated type:
use std::rc::Rc;
pub trait Widget {
// stuff related to widgets
}
// All `MeasurableWidget`s are `Widget`s
pub trait MeasurableWidget: Widget {
// allow the widget to be measured
}
pub struct ScrollBox {
// stuff related to scrollboxes
}
pub trait Layout {
type ChildRef;
fn insert_child(&mut self, child: Self::ChildRef);
// more stuff related to layouts
}
impl Layout for ScrollBox {
type ChildRef = Rc<dyn MeasurableWidget>;
fn insert_child(&mut self, child: Self::ChildRef) {
// insert magic here
}
}

Welp, several hours of intense Rust sorcery has led to to a solution:
Instead of attempting to coerce MeasurableWidgets into Widgets, I instead make the type of the child a Generic parameter, I can specify which I need at the time of instantiation (OOP lingo to the rescue)
pub trait Layout<T: Widget> {
fn insert_child(&mut self, child: Rc<T>)
// more stuff
}
By doing this, I cause the compiler to inform me of unknown sizes at compile time:
error[E0277]: the size for values of type `(dyn Widget + 'static)` cannot be known at compilation time
However, by removing the Sized trait from the type bound, the compiler seems to accept this.
pub trait Layout<T: Widget + ?Sized> {
fn insert_child(&mut self, child: Rc<T>)
// more stuff
}
Now, I will admit that I don't like this approach as there's clearly something strange happening, which I don't understand. If anyone can shed some light into what's going on in that example and why it works, please let me know.
I'd like to also mention that #Finomnis ' answer is getting close. But the same + ?Sized issue arises. Thanks so much for your help regardless.

Related

How do I avoid Enum + Trait pattern when a struct is not object safe?

I get the implications of object safety, but I'm trying to find an idiomatic way to solve for this situation.
Say I have two structs that share common behavior and also need to derive PartialEq for comparison in another part of the program:
trait Growl:PartialEq {
fn growl(&self);
}
#[derive(PartialEq)]
struct Pikachu;
#[derive(PartialEq)]
struct Porygon;
impl Growl for Pikachu {
fn growl(&self) {
println!("pika");
}
}
impl Growl for Porygon {
fn growl(&self) {
println!("umm.. rawr?");
}
}
In another struct, I want to hold a Vec of these objects. Since I can't use a trait object with Vec<Box<Growl>>...
struct Region{
pokemon: Vec<Box<dyn Growl>>,
}
// ERROR: `Growl` cannot be made into an object
... I need to get more creative. I read this article, which suggests using an enum or changing the trait. I haven't yet explored type erasure, but it seems heavy-handed for my use case. Using an enum like this is what I've ended up doing but it feels unnecessarily complex
enum Pokemon {
Pika(Pikachu),
Pory(Porygon),
}
Someone coming through this code in the future now needs to understand the individual structs, the trait (which provides all functionality for the structs), and the wrapper enum type to make changes.
Is there a better solution for this pattern?
I read this article, which suggests using an enum or changing the trait. I haven't yet explored type erasure, but it seems heavy-handed for my use case.
Type erasure is just a synonym term for dynamic dispatch - even your original Box<dyn Growl> "erases the type" of the Pokemon. What you want here is to continue in the same vein, by creating a new trait better taylored to your use case and providing a blanket implementation of that trait for any type that implements the original trait.
It sounds complex, but it's actually very simple, much simpler than erased-serde, which has to deal with serde's behemoth traits. Let's go through it step by step. First, you create a trait that won't cause issues with dynamic dispatch:
/// Like Growl, but without PartialEq
trait Gnarl {
// here you'd have only the methods which are actually needed by Region::pokemon.
// Let's assume it needs growl().
fn growl(&self);
}
Then, provide a blanket implementation of your new Gnarl trait for all types that implement the original Growl:
impl<T> Gnarl for T
where
T: Growl,
{
fn growl(&self) {
// Here in the implementation `self` is known to implement `Growl`,
// so you can make use of the full `Growl` functionality, *including*
// things not exposed to `Gnarl` like PartialEq
<T as Growl>::growl(self);
}
}
Finally, use the new trait to create type-erased pokemon:
struct Region {
pokemon: Vec<Box<dyn Gnarl>>,
}
fn main() {
let _region = Region {
pokemon: vec![Box::new(Pikachu), Box::new(Porygon)],
};
}
Playground

Overriding or dynamic dispatch equivalent in rust

I've been learning rust for a while and loving it. I've hit a wall though trying to do something which ought to be simple and elegant so I'm sure I'm missing the obvious.
So I'm parsing JavaScript using the excellent RESSA crate and end up with an AST which is a graph of structs defined by the crate. Now I need to traverse this many times and 'visit' certain nodes with my logic. So I've written a traverser that does that but when it hits a certain nodes it needs to call a callback. In my niavity, I thought I'd define a struct with an attribute for every type with an Option<Fn()> value. In my traverser, I check for the Some value and call it. This works fine but it's ugly because I have to populate this enormous struct with dozens of attributes most of which are None because I'm not interested in those types. Then I thought traits, I'd define a trait 'Visit' which defines the function with a default implementation that does nothing. Then I can just redefine the trait implementation with my desired implementation but this is no good because all the types must have an implementation and then the implementation cannot be redefined. Is there as nice way I can just provide a specific implementation for a few types and leave the rest as default or check for the existence of a function before calling it ? I must be missing an idiomatic way to do this.
You can look at something like syn::Visit, which is a visitor in a popular Rust AST library, for inspiration.
The Visit trait is implemented by the visitor only, and has one method for each node type, with the default implementation only visiting the children:
// this snippet has been slightly altered from the source
pub trait Visit<'ast> {
fn visit_expr(&mut self, i: &'ast Expr) {
visit_expr(self, i);
}
fn visit_expr_array(&mut self, i: &'ast ExprArray) {
visit_expr_array(self, i);
}
fn visit_expr_assign(&mut self, i: &'ast ExprAssign) {
visit_expr_assign(self, i);
}
// ...
}
pub fn visit_expr<'ast, V>(v: &mut V, node: &'ast Expr)
where
V: Visit<'ast> + ?Sized,
{
match node {
Expr::Array(_binding_0) => v.visit_expr_array(_binding_0),
Expr::Assign(_binding_0) => v.visit_expr_assign(_binding_0),
// ...
}
}
pub fn visit_expr_array<'ast, V>(v: &mut V, node: &'ast ExprArray)
where
V: Visit<'ast> + ?Sized,
{
for el in &node.elems {
v.visit_expr(el);
}
}
// ...
With this pattern, you can create a visitor where you only implement the methods you need, and whatever you don't implement will just get the default behavior.
Additionally, because the default methods call separate functions that do the default behavior, you can call those within your custom visitor methods if you need to invoke the default behavior of visiting the children. (Rust doesn't let you invoke default implementations of an overriden trait method directly.)
So for example, a visitor to print all array expressions in a Rust program using syn::Visit could look like:
struct MyVisitor;
impl Visit<'ast> for MyVisitor {
fn visit_expr_array(&mut self, i: &'ast ExprArray) {
println("{:?}", i);
// call default visitor method to visit this node's children as well
visit_expr_array(i);
}
}
fn main() {
let root = get_ast_root_node();
MyVisitor.visit_expr(&root);
}

Problem calling a trait method of a struct in a vector

What I want to do
The method in this case is Flower::update. I need to call update on a Flower in this case, in a loop. The loop is in Garden::update_all(), and this loop defines flower and then tries to call update on that flower but does not work.
What I have tried but why it hasen't work
I have looked at E0507, and other articles about similar problems. The ones that seem like the might help mention that I need to implement Copy on flowers, so the vector of Flowers, so Flower also needs to implement Copy but can't because the trait 'Copy' may not be implemented for this type.
Also, the problem is that I don't want to copy the data, I want to call a method on the reference. Like flower[0].update; or something similar. I don't need to use the data, only call a method that changes it.
What my best guess is
It has something to do with self and it's mutability and how I look through flowers and assign a single Flower to a variable flower and try to call a method on a created mutable reference. But I'm not sure. Should the method have &mut self or &self? Should the loop make a new mut flower, or just a normal flower, Should the loop be &self.flowers or self.flowers. I've tried many variation and I think it might be some combination of these, and an extra thing I don't know about.
pub trait FlowerTrait {
fn update(&mut self);
}
pub struct Flower {
pub number: i32,
}
impl FlowerTrait for Flower {
fn update(&mut self) {
self.number += 1;
}
}
pub trait GardenTrait {
fn update_all(&mut self);
}
pub struct Garden {
pub flowers: Vec<Flower>,
}
impl GardenTrait for Garden {
fn update_all(&mut self) {
for mut flower in self.flowers {
flower.update();
}
}
}
fn main() {
let mut garden: Garden = Garden {
flowers: Vec::<Flower>::new(),
};
garden.update_all();
}
For a better post, you should have included the error message you get.
The problem occurs because a for loop behind the scenes uses the into_iterator method from the IntoIterator trait, and conversion is consuming, i.e., a move occurs.
The solution is to iterate over references instead. Simply change your for loop to
for flower in &mut self.flowers {
// ...
}
Or alternatively
for flower in self.flowers.iter_mut() {
// ...
}
That creates an iterator over references (borrows) instead.
See here: rust playground

Rust subtyping/"runtime polymorphism"/using traits in Boxes

Consider the following dumbed-down example:
pub trait ThemePark<A, V>
where
A: Attraction,
V: Visitor,
{
fn create(square_size: u32, name: &str) -> Self;
fn get_attractions(&self) -> Vec<A>;
fn get_visitors(&self) -> Vec<V>;
}
pub trait Attraction {}
pub trait Visitor {}
A concrete ThemePark could have an arbitrary set of attractions, as well as visitors. They are implemented with structs:
struct ElderlyWhiteMale;
impl Visitor for ElderlyWhiteMale {}
A ThemePark is wrapped in some company's assets, like so:
pub struct Asset<'a> {
name: &str,
theme_park: Box<ThemePark<> + 'a> // <-- This won't compile, as ThemePark needs 2 type arguments
}
This begins my pain. I put ThemePark in a Box because I don't know the size of it at compile time. It could be wrapped around any kind of Attraction and Visitor.
ThemePark needs 2 type arguments, but I can't know them at compile-time. Somewhere in my code I read this from an external file and build a ThemePark accordingly.
The idea is, that at runtime I can create a ThemePark from an external source and then invoke the functions defined in the trait on it.
impl Asset {
fn init_and_query() -> () {
let theme_park: Box<ThemePark> = match external_file.get_theme_park_type {
ThemeParkType::FUN => unimplemented! {"There is no fun, yet!"},
ThemeParkType::SERIOUS => {
println!("Creating serious themepark");
SeriousThemePark::create(size /*...*/)
}
};
let attractions = theme_park.get_attractions();
// ... Do something with the attractions
}
}
pub enum ThemeParkType {
FUN,
SERIOUS,
}
I understand that I can't put the ThemePark as-is on the stack... it's size is unknown at compile time, so the compiler can't know what to allocate.
That's why I either use a reference & or wrap it in a Box like I do here.
I understand there is type erasure, meaning that I would get back only a ThemePark and not a SeriousThemePark, but that would suffice for the moment.
Am I using traits all wrong here? How would you go and fix that. Coming from Java/Scala/C++ I seem to be stuck too deep in existing thinking.
Rust polymorphism is very similar to C++ polymorphism in that regard; it features:
compile-time polymorphism, to parameterize an item with types known at compile-time,
run-time polymorphism, when the concrete types are not known at compile-time.
Rust uses trait to define an interface which is then used to both constrain compile-time type parameters and serve as base-class/interface for run-time polymorphism, which is perhaps where your confusion comes from, however both kinds of polymorphism are inherently different.
pub struct Asset<'a> {
name: &str,
theme_park: Box<ThemePark<> + 'a> // <-- This won't compile, as ThemePark needs 2 type arguments
}
Then you should NOT be using compile-time polymorphism, and instead define ThemePark as:
pub trait ThemePark {
fn create(square_size: u32, name: &str) -> Self;
fn get_attractions(&self) -> Vec<Box<Attraction>>;
fn get_visitors(&self) -> Vec<Box<Visitor>>;
}
By instantiating a ThemePark<A, V> you create a theme park which can only ever contain one type of attractions (it's all Haunted Houses here, no Flume Ride sorry!) and one type of visitors (only Elderly Guys in, no Elderly Ladies or Kids).

Static method in trait dynamic dispatch

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);
// ...
}

Resources