I am trying to implement very simple polymorphism, that exists in any OOP language, by defining a "behaviour" and then having structs that implement this behaviour differently
But then, as I try to produce instance of a "dyn trait" object, I can't think of how to do it:
You can't hold a dyn Trait value by-value, because it's dynamically sized. You'll have to put it behind some indirection, e.g. Box (if you want to own it) or & (if you don't need to).
You can't have a bare trait object like that. It needs to be behind a pointer of some sort.
let mut evaluator: Box<dyn ClassNameEvaluator> =
match cli.luaclassfilter.as_ref() {
Some(code) => Box::new(LuaEvaluator::new(code)),
None => Box::new(dummy),
};
The error you're getting basically means "you're using a concrete struct but none of the coercion rules for changing into a trait object applied". And that's because they only apply to types that can contain a trait object like Box, Arc, and references
You'll need the Box here. You are creating the value, so you own it, you have to hold onto it if you want a reference to it. References in themselves don't cause values to stick around.
The presence of a reference to a value necessarily means that someone, somewhere, owns that value.
References don't own values. You own things by value.
You can only own something if you know its static size at compile tome. This is not obvious, as this is just a current technical requirement in Rust, which may be lifted in the future.
dyn Trait is dynamically sized, so you have to put it behind indirection in order to own it.
It's possible to own stuff and put it behind indirection, but that's not what references are for. Owning heap-allocating smart pointers include Box (when you want a single owner) and Rc/Arc (when you want multiple, refcounted owners).
Therefore, when you create a trait object that you want to own, you have to put it in Box or Rc or Arc.
But if you already have a boxed or refcounted (ie. owned) trait object, you can pass a mere reference to it into another function.
That is, there is nothing special about your situation/error wrt. trait objects; ownership and borrowing rules apply in exactly the same way to trait objects as they apply to any other type. What tripped you up is that you wanted to create a dynamically-sized value on the stack, which is not possible.
Someone needs to go about writing "Rust misconceptions" book I can see at least 3 audiences with their own pre-conceived expectations about "how programming works" approaching Rust and having different, but still quite typical, issues :
people from managed runtimes/languages, like Java/C#
people from dynamically typed languages, like node.js, Python, Ruby
people from C/C++ background
I kinda knew about ownership and references, and have some scars from fighting the BC in the past, but never used traits yet, at least not directly, so yeah, that was an exercise
use std::rc::Rc;
trait SomeTrait {
fn hello(&self);
}
struct SomeStruct { }
impl SomeTrait for SomeStruct {
fn hello(&self) { println!("hello"); }
}
fn do_hello(trait_obj : Rc<dyn SomeTrait>) {
trait_obj.hello()
}
fn main() {
let a = Rc::new(SomeStruct{} );
let b = Rc::clone(&a);
do_hello( b ); // This compiles ok
do_hello( (|| Rc::clone(&a))() ); // Weird, but compiles
do_hello( a.clone() ); // still ok
// do_hello( Rc::clone(&a) ); // Why is this an error?
}
After reading the replies above, I cannot explain why the three first calls to do_hello compile ok, but the fourth one does not. I am under the impression that Rc::clone(&a) is just an alternative syntax for a.clone(), so they should be semantically the same.
In the first three cases, a clone of Rc<SomeStruct> happens, and is then coerced to Rc<dyn SomeTrait>. In the forth case, the compiler tries to figure out whichRc::<_>::clone you're trying to call and -- based on the expected argument type -- wrongly decides on Rc::<dyn SomeTrait>::clone. It works if you correct it.
The difference from a.clone() is the lack of a generic type variable to infer, and the difference with the other cases is how far inference decides to look before falling back to a's type (or not).
My understanding is now that the code is not actually wrong, but this is just a shortcoming of the current type inference algorithm. So the compiler flags this as an error just to be on the safe side. But this may change as the compiler evolves.
Yep, there's so much instances of such behaviour in rust for newcomers))) I so often struggle on some code and juggle bits around based on the suggestions from error messages, until BOOM it just compiles ))) and when compiled it mostly just works
The code is not technically wrong, but if you manage to confuse the compiler with it, imagine what it does to human brains. I wouldn't say the compiler is at fault here. You should really try to write your code in a way that it's unambiguous to a programmer too – and this instance clearly isn't.
Don't try to be excessively "clever" with inference (or any other language construct). These compiler non-bugs are a gentle reminder that you may be trying too hard to impress fellow programmers or the Rust Gods.
The compiler isn't trying to second-guess you. If anything, this should be reassuring and not "spooky".
I work a lot with automated systems (let's not call them "AI") where medical decisions are being made based on a statistical model's opinion of a physiological measurement, such as blood pressure or ECG. Of course, measurements can be noisy, absent, or just downright wrong.
What should a model decide to do when it doesn't know if the measurement is correct? Send the patient to 25 different kinds of surgeries, just to be safe? Surely that would not be a splendid idea. It should just tell the doctor that it should see patient no. 1337 because his/her measurements look not quite OK.
The point is, it's vastly better for an automated system to have a well-calibrated sense of confidence, and bail out when it's obviously unsure, instead of trying hard to make a decision and screw up big time.
Inference coupled with coercions is hard. The compiler is intentionally not trying to see two steps into the future. Rust is protecting your life – instead of sending you to a potentially harmful surgery, it tells you to see a doctor.
I get what you are saying, and in general, I fully agree. Code should be clear and readable, unless you are participating in an obfuscated code contest.
However, this code is not trickery to confuse or impress anyone. While a.clone() may be the most straightforward way to write this, the problematic Rc::clone(&a) is the form recommended in the documentation of Rc. The reason for this recommendation is that it more clearly states that the cloning here applies to the Rc only, not the referenced object. This is, IMHO, a good recommendation, and there is just a small gap in the type inference implementation.