pub trait Visitor {
fn visit_program(&self, node: &ProgramNode) where Self: Sized {
println!("Visit Program");
for child in &node.base.children {
child.accept(self)
}
}
fn visit_variable_declaration(&self, node: &Variable_declarationNode) where Self: Sized {
println!("Visit Variable_declaration");
for child in &node.base.children {
child.accept(self)
}
}
..... lots more
child is &Box<dyn IBaseNode>
That code works fine if I implement visit_program in a concrete implementation of Visitor, but will not compile here. It complains about self being unsized. The compiler suggested adding the where Self :Sized, but that just produces a 'no can do ' error
error: the `visit_program` method cannot be invoked on a trait object
--> horn_lox\src\loxvisitor.rs:203:17
|
63 | fn visit_program(&self, node: &ProgramNode) where Self: Sized {
| ----- this has a `Sized` requirement
...
203 | visitor.visit_program(self);
Sorry if a dup, I did hunt about trying to find pointers
// vv
// The compiler provides an implementation for `dyn AsVisitor + '_`
pub trait AsVisitor {
// The compiler also provides `impl AsVisitor for dyn Visitor + '_`
// due to the supertrait bound
pub trait Visitor: AsVisitor {
Thanks you very much, works, need to contemplate it to internalize why it works (who said rust is complicated - hah). Having got this working I realized that I do not need any dynamic dispatch on the Visitor type. There will only every be one of them in any mesh of these objects, so I changed
But that doesnt work either, complaining that this trait is not object safe.Since I prefer to keep unneeded dynamic stuff minimized I wonder how that could be made to work.
It's basically the same approach as manual supertrait upcasting. You have to do a little dance to make the coercion available (as a method) without restricting it to Sized types (which would make the method non-dyn-dispatchable).
(Note how this doesn't mean you accept "one specific Visitor", it means you accept "any Visitor"; the caller still chooses the type. There are also some subtleties around the fact you take by value now; for example, you can't take &dyn Visitor or Box<dyn Visitor> unless you implement Visitor for those types.)
What you had originally (&dyn Visitor) is a reasonable way to approximate a generic function in an object-safe way.
(Sometimes using an associated type is an option, depending on what you meant by "only ever be one of them", but in this case my intuition is that it infects your dyn IBaseNode too much.
once again , thank you. By 'one one of them' I meant that there will only be one type of Visitor, so I dont need to dynamically dispatch on it. That type will be decided by the implementor of the trait
I am sure I am going to be posting a follow up on associated types shortly, I need the visit_xxx methods to return an implementor decidable type and cannot quite work out how to do it (seems like it should be simple - hah ,always run into odd things trying to coerce rust into being an OO language). I'll be back
Correct, if you need the method to be object safe, it can't have a generic type parameter (no <T: Visitor> and no arg: impl Visitor (which is basically the same thing)).
That's what I was trying to say in the second half of my reply above - they're the same thing, and make the method non-object-safe.