I'm having a trouble with stored references to trait objects and hoped someone might clear my confusion. I'm trying to implement a data type which contains either an owned or a borrowed trait object. While implementing a function that extracts a reference I got stuck with an error. I'm not sure what exactly it means - for me the code looks straightforward as I just return the stored reference. Here's my example:
Compiling playground v0.0.1 (/playground)
error[E0277]: the trait bound `&dyn Operation: Operation` is not satisfied
--> src/lib.rs:10:39
|
10 | OperationBox::Borrowed(op) => op,
| ^^ the trait `Operation` is not implemented for `&dyn Operation`
|
= note: required for the cast to the object type `dyn Operation`
error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
error: could not compile `playground`.
To learn more, run the command again with --verbose.
My target aim is to implement AsRef<dyn Operation> for this type. What do I miss here? Thanks in advance for any help!
I think the issue is that you have a reference to an OperationBox, and then you are returning a reference to a field of that OperationBox that contains a reference, so the type of op in the Borrowed case is && dyn Operation. The double reference is confusing Rust when it tries to do the coercion to the output type. The solution is simply to dereference op:
fn get_op<'a>(op: &'a OperationBox<'a>) -> &'a dyn Operation {
match op {
OperationBox::Borrowed(op) => *op,
OperationBox::Owned(op) => op.as_ref(),
}
}
It works! Haha, I completely forgot that you can dereference this way in Rust. I tried to do this with OperationBox::Borrowed(&op) before, but got an error about dereferencing dyn (which seemed logical to me).
If I may ask, what is the difference between OperationBox::Borrowed(&op) => op and OperationBox::Borrowed(op) => *op?
I think what is happening in the pattern match OperationBox::Borrowed(&op), is that, it is trying to bind op to the inner value of the double reference, so it is effectively dereferencing twice. That ends up producing a dyn Operation, which is unsized, so Rust doesn't let you do that.
The explicit dereference only dereferences once, so it's fine.
This is a result of something called match ergonomics, which tries to make match expressions easier to use by not requiring explicit references and dereferences, but ends up being confusing when it incorrectly guesses what you're trying to do. You can do it with a pattern match, but you need to match against the outer reference rather than the inner one:
fn get_op<'a>(op: &'a OperationBox<'a>) -> &'a dyn Operation {
match op {
&OperationBox::Borrowed(op) => op,
OperationBox::Owned(op) => op.as_ref(),
}
}