Passing boxed trait object to function accepting generic parameter implementing the trait - rust

I have a function which returns a boxed trait object, and another function that accepts a reference to an object implementing the same trait. I would like to pass a reference to the boxed trait object to the second function, but I am unable to figure out how to do this.
Example simplified code:
trait MyTrait {
fn foo(&self);
}
struct A {}
impl MyTrait for A {
fn foo(&self) {
println!("A");
}
}
struct B {}
impl MyTrait for B{
fn foo(&self) {
println!("B");
}
}
enum MyEnum {
A,
B,
}
fn create_object(my_enum: MyEnum) -> Box<dyn MyTrait> {
let boxed_value: Box<dyn MyTrait> = match my_enum {
MyEnum::A => Box::new(A{}),
MyEnum::B => Box::new(B{}),
};
boxed_value
}
fn do_something<T: MyTrait>(obj: &T) {
obj.foo();
}
fn main() {
use std::borrow::BorrowMut;
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value.borrow_mut());
}
The error I get:
error[E0282]: type annotations needed
--> src\main.rs:42:5
|
42 | do_something(boxed_value.borrow_mut());
| ^^^^^^^^^^^^ ------------------------ this method call resolves to `&mut Borrowed`
| |
| cannot infer type for type parameter `T` declared on the function `do_something`
Intuitively, I would have hoped that in this case Rust would use dynamic dispatch and wouldn't care about the concrete type T (similarly to what happens in C++ when you pass a reference to a base class), but this seems not to be the case.
How do I pass a reference to the boxed trait object (Box<dyn MyTrait>) to the second function (do_something)? Is this possible in some way? A solution requiring a change to do_something would also be acceptable.

Intuitively, I would have hoped that in this case Rust would use dynamic dispatch and wouldn't care about the concrete type T (similarly to what happens in C++ when you pass a reference to a base class), but this seems not to be the case.
You can make that happen with a cast (or just type ascription, eventually) and by relaxing the default requirement for T to be Sized:
fn do_something<T: MyTrait + ?Sized>(obj: &T) {
obj.foo();
}
use std::borrow::Borrow;
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value.borrow() as &dyn MyTrait);
But if you’re not otherwise using T, you can opt into dynamic dispatch on the function side much more simply:
fn do_something(obj: &dyn Borrow) {
obj.foo();
}
use std::borrow::Borrow;
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value.borrow());
And if you don’t care that obj is a borrow and want to leave the option of static dispatch open, you can implement MyTrait for &dyn MyTrait:
impl MyTrait for &dyn MyTrait {
fn foo(&self) {
(*self).foo();
}
}
fn do_something<T: MyTrait>(obj: T) {
obj.foo();
}
// or, again, if not otherwise using T:
fn do_something(obj: impl MyTrait) {
obj.foo();
}
use std::borrow::Borrow;
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value.borrow());

No matter what, you'll need to add ?Sized to the trait bound in do_something, and then I think you have one of three options:
(Least general) Use as_ref() on the Box when you call do_something.
fn do_something<T: MyTrait + ?Sized>(obj: &T) {
obj.foo();
}
fn main() {
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value.as_ref());
}
(Most general) Replace the type of obj in do_something with impl AsRef<T>. This will make do_something work with anything convertible to a &T.
fn do_something<T: MyTrait + ?Sized>(obj: impl AsRef<T>) {
obj.as_ref().foo();
}
fn main() {
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value);
}
(Medium general) Replace the type of obj in do_something with impl Deref<Target=T>. This will make do_something work with any smart pointer holding a T (which is a bit more restrictive than AsRef<T> — a type can implement AsRef<T> for as many values of T as it wants, but only gets to have one Deref implementation).
use std::ops::Deref;
fn do_something<T: MyTrait + ?Sized>(obj: impl Deref<Target=T>) {
obj.deref().foo();
}
fn main() {
let boxed_value = create_object(MyEnum::A);
do_something(boxed_value);
}

Instead of trying to unbox the value
you can instead implement MyTrait on Box<dyn MyTrait>
and forward to the boxed value.
impl MyTrait for Box<dyn MyTrait> {
fn foo(&self) {
self.deref().foo()
}
}
Then you don't even need to call borrow_mut.
fn main() {
use std::borrow::BorrowMut;
let boxed_value = create_object(MyEnum::A);
do_something(&boxed_value);
}
There's a working example in the playground

Related

`AsArray` cannot be made into an object when implementing a trait for a trait

Basically I'm trying to make a trait that indicates the ability to be converted into a 2D ndarray aka ndarray::Array2:
trait Into2DArray{
fn to_array(&self) -> Array2<f64>;
}
I would like to do this by expanding the existing AsArray trait, but Rust forbids me from implementing a third party trait for a third party struct (polars::DataFrame) for some esoteric reason, so instead I have to make my own trait for this.
Anyway, this works well for polars::DataFrame:
impl Into2DArray for DataFrame {
fn to_array(&self) -> Array2<f64> {
return self.to_array();
}
}
However, I also want to implement this for anything that is already convertable into a 2D array, so I implement this trait for the AsArray trait mentioned above:
impl Into2DArray for AsArray<'_, f64, Ix2> {
fn to_array(&self) -> Array2<f64> {
return self.into();
}
}
However the compiler gives me grief for this:
|
26 | impl Into2DArray for AsArray<'_, f64, Ix2> {
| ^^^^^^^^^^^^^^^^^^^^^ `AsArray` cannot be made into an object
|
= note: the trait cannot be made into an object because it requires `Self: Sized`
= note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
I understand that has something to do with object safety but I thought I had fulfilled all the criteria mentioned on that page, namely the trait doesn't return Self, and all the generic parameters of AsArray are specified.
What is going wrong, and how can I fix it?
What you were trying to do is implementing the Into2DArray trait for the AsArray dynamic trait object. There should have been a warning of using AsArray without dyn anyway.
But this is not what you actually want. You want to implement it for any type that implements AsArray. Just like you did in your comment.
It is important to know the difference between these two things:
trait NeedThis {
fn can_be_called_by_the_impl(&self) {}
}
trait ToDoThis {
fn example(&self);
}
impl ToDoThis for dyn NeedThis {
fn example(&self) {
self.can_be_called_by_the_impl()
}
}
impl NeedThis for u8 {}
fn main() {
let num: u8 = 0;
// num.example(); // doesn't work because ToDoThis is not implemented for u8
let num_as_trait_obj: &dyn NeedThis = &0_u8 as &dyn NeedThis;
num_as_trait_obj.example(); // works because this time it is a trait object
}
trait NeedThis {
fn can_be_called_by_the_impl(&self) {}
}
trait ToDoThis {
fn example(&self);
}
// removing ?Sized would make it the same as T: NeedThis + Sized
impl<T: NeedThis + ?Sized> ToDoThis for T {
fn example(&self) {
self.can_be_called_by_the_impl()
}
}
impl NeedThis for u8 {}
fn main() {
let num: u8 = 0_u8;
num.example(); // works because we implemented it for all types that implement NeedThis
let num_as_trait_obj: &dyn NeedThis = &0_u8 as &dyn NeedThis;
num_as_trait_obj.example(); // works because dyn NeedThis also implements NeedThis.
// This is only true because we added ?Sized to the bounds of the impl block.
// Otherwise it doesn't work because dyn NeedThis is not actually Sized.
// And a Sized bound is implied by default.
}

closure implementing FnMut not working on place where dyn FnMut is expected

use std::cell::RefCell;
use std::rc::Rc;
use wasm_bindgen::prelude::*;
use wasm_bindgen::JsCast;
fn main() {
let closure_cell = Rc::new(RefCell::new(None));
let closure_cell_clone = Rc::clone(&closure_cell);
*closure_cell.borrow_mut() = Some(Closure::wrap(Box::new(move || {
request_animation_frame(closure_cell_clone.borrow().as_ref().unwrap());
})));
}
fn request_animation_frame(f: &Closure<dyn FnMut()>) {
}
In the above code request_animation_frame expects &Closure<dyn FnMut()>and I pass a closure which implements FnMnut. But I am getting an error
expected type `std::option::Option<wasm_bindgen::closure::Closure<dyn std::ops::FnMut()>>`
found type `std::option::Option<wasm_bindgen::closure::Closure<[closure#src/lib.rs:29:62: 39:6 bucket:_, world:_, closure_cell_clone:_]>>`
Arent FnMut default implemented if a closure can implement that trait? Cant a closure which implements FnMut be sent in place of dyn FnMut ? What am I missing here?
This is actually a pretty sneaky problem. Here's why:
While you can coerce a Box<T> to a Box<dyn Trait> if T: Trait, those two are incompatible types, in that you cannot use a variable or field of the type Box<T> as a Box<dyn Trait>.
Here's an example to show why that could lead to undefined behaviour, using Cell<...>s (where that box would be a field somewhere in Cell<...>)
use std::cell::Cell;
trait SomeTrait { }
#[derive(Clone, Copy)]
pub struct A;
impl SomeTrait for A { }
#[derive(Clone, Copy)]
pub struct B;
impl SomeTrait for B { }
fn use_coerced(cell: &Cell<Box<dyn SomeTrait>>) {
cell.set(Box::new(B)); // valid, since `B` implements `SomeTrait`
}
fn main() {
// I'll put what the type inference will come up with as explicit types here
let some_cell: Cell<Box<A>> = Cell::new(Box::new(A));
use_coerced(&some_cell); // If cells would coerce, this would be valid
let value: Box<A> = some_cell.into_inner(); // But we put `Box<B>` in there. So we'd have undefined behaviour.
}
So to solve that problem, you have to tell the compiler to use the dyn FnMut() type instead of the closure type somewhere. I personally prefer explicitely unsizing it after creating the Box:
fn main() {
let closure_cell = Rc::new(RefCell::new(None));
let closure_cell_clone = Rc::clone(&closure_cell);
*closure_cell.borrow_mut() = Some(Closure::wrap(Box::new(move || {
request_animation_frame(closure_cell_clone.borrow().as_ref().unwrap());
}) as Box<dyn FnMut()>));
}
fn request_animation_frame(f: &Closure<dyn FnMut()>) { }
You can, however, also specify the type of the cell:
fn main() {
let closure_cell: Rc<RefCell<Option<Closure<dyn FnMut()>>>> = Rc::new(RefCell::new(None));
let closure_cell_clone = Rc::clone(&closure_cell);
*closure_cell.borrow_mut() = Some(Closure::wrap(Box::new(move || {
request_animation_frame(closure_cell_clone.borrow().as_ref().unwrap());
})));
}
fn request_animation_frame(f: &Closure<dyn FnMut()>) { }
But that is, as you can see, way more wordy, and unnecessarily so.

Why sized trait is required for a builder function to generate Rc<T>?

This code works fine (playground):
use std::rc::Rc;
trait Foo {
fn foo(&self);
}
struct Bar<T> {
v: Rc<T>,
}
impl<T> Bar<T> where
T: Foo {
fn new(rhs: Rc<T>) -> Bar<T> {
Bar{v: rhs}
}
}
struct Zzz {
}
impl Zzz {
fn new() -> Zzz {
Zzz{}
}
}
impl Foo for Zzz {
fn foo(&self) {
println!("Zzz foo");
}
}
fn make_foo() -> Rc<Foo> {
Rc::new(Zzz{})
}
fn main() {
let a = Bar::new(Rc::new(Zzz::new()));
a.v.as_ref().foo()
}
but if I make a wrapper to generate Rc like below, the compiler complains about missing sized trait (playground)
fn make_foo() -> Rc<dyn Foo> {
Rc::new(Zzz::new())
}
fn main() {
let a = Bar::new(make_foo());
a.v.as_ref().foo()
}
in both cases, Bar::new received parameters with same type Rc, why the rust compiler reacts different?
By default, all type variables are assumed to be Sized. For example, in the definition of the Bar struct, there is an implicit Sized constraint, like this:
struct Bar<T: Sized> {
v: Rc<T>,
}
The object dyn Foo cannot be Sized since each possible implementation of Foo could have a different size, so there isn't one size that can be chosen. But you are trying to instantiate a Bar<dyn Foo>.
The fix is to opt out of the Sized trait for T:
struct Bar<T: ?Sized> {
v: Rc<T>,
}
And also in the context of the implementations:
impl<T: ?Sized> Bar<T>
where
T: Foo
?Sized is actually not a constraint, but relaxing the existing Sized constraint, so that it is not required.
A consequence of opting out of Sized is that none of Bar's methods from that impl block can use T, except by reference.

Why do I get "use of undeclared type or module" error when calling a struct's function?

I have the following code in Rust:
trait MyTrait {
fn get_value() -> &'static str;
}
#[derive(Debug)]
struct MyStruct;
impl MyTrait for MyStruct {
fn get_value() -> &'static str {
"has value"
}
}
fn main() {
println!("My value: {}", MyStruct::get_value());
has_trait(MyStruct);
}
fn has_trait<T>(trt: T) where T: MyTrait + std::fmt::Debug {
println!("{:?}", trt)
}
This code is fine. It defines a trait and a struct. The struct implements the trait; which requires to implement a function. Everything is fine until now. But if I try the following code:
trait MyTrait {
fn get_value() -> &'static str;
}
#[derive(Debug)]
struct MyStruct;
impl MyTrait for MyStruct {
fn get_value() -> &'static str {
"has value"
}
}
fn main() {
println!("My value: {}", MyStruct::get_value());
has_trait(MyStruct);
}
fn has_trait<T>(trt: T) where T: MyTrait + std::fmt::Debug {
println!("{:?}", trt::get_value())
}
I get the following error:
error[E0433]: failed to resolve: use of undeclared type or module `trt`
--> src/main.rs:21:22
|
21 | println!("{:?}", trt::get_value())
| ^^^ use of undeclared type or module `trt`
Now, I don't really understand very well why that wouldn't work. trt should represent a copy of myStruct and then it should have its own functions, right?
Interestingly, this following code will compile:
trait MyTrait {
fn get_value(&self) -> &'static str;
}
#[derive(Debug)]
struct MyStruct;
impl MyTrait for MyStruct {
fn get_value(&self) -> &'static str {
"has value"
}
}
fn main() {
println!("My value: {}", MyStruct.get_value());
has_trait(MyStruct);
}
fn has_trait<T>(trt: T) where T: MyTrait + std::fmt::Debug {
println!("{:?}", trt.get_value())
}
So what is exactly wrong with the code that doesn't compile?
Now, I don't really understand very well why that wouldn't work. trt should represent a copy of MyStruct and then it should have its own functions, right?
It doesn't quite work that way for associated functions in Rust.
With the identifier trt, you can call methods where trt is the receiver (self or one of its variations such as &self or &mut self). However, get_value() does not have a receiver, so it is an associated function. This resembles a static method in some languages such as Java. Unlike Java, associated functions in Rust can only be called by specifying the type or type parameter with that function:
fn has_trait<T>(trt: T) where T: MyTrait + std::fmt::Debug {
println!("{:?}", T::get_value())
}
This will now work, and would not even need the parameter trt, because we're just calling an associated function of the type T, rather than a method.
Although trt is an identifier to a function parameter in this context, the compiler will actually try to interpret it as something else (module name, type name, ...) once combined with the :: token, hence the given error message.

Provided method casting &self to trait object

I have a trait in which I want to provide a method. The method is to be implemented in terms of some helpers that have no business being inside the trait and are non-trivial enough that dynamic polymorphism makes more sense than making them generic. So I have code along the lines of
fn use_trait(x: &Trait) {
println!("object says {}", x.needed());
}
trait Trait {
fn needed(&self) -> &str;
fn provided(&self) {
use_trait(self);
}
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
}
fn main() {
Struct().provided();
}
Which, however, does not compile, with error:
error[E0277]: the trait bound `Self: std::marker::Sized` is not satisfied
--> <anon>:9:19
|
9 | use_trait(self);
| ^^^^ the trait `std::marker::Sized` is not implemented for `Self`
|
= help: consider adding a `where Self: std::marker::Sized` bound
= note: required for the cast to the object type `Trait`
I understand why—it is not guaranteed somebody won't implement the trait for an unsized type (converting from &T where T: Trait to &Trait requires T: Sized, but the declaration does not require that).
However, the advice will not do what I need. I can add
fn needed(&self) -> &str where Self: Sized
but then the needed() method won't be accessible on &Trait (because Trait : ?Sized), which renders the thing useless, because the type (the actual one that does something useful) is always handled as Arc<Trait>. And adding
trait Trait: Sized
is even worse, because that does not permit &Trait at all (Trait as a type is unsized, so Trait type does not implement trait Trait).
Of course I can simply make
fn use_trait<T: Trait>(x: &T)
but there is a lot behind it in the real code, so I don't want monomorphisation there especially since the trait is otherwise always handled as trait object.
Is there any way to tell Rust that all types that impl Trait must be sized and here is a definition of a method that should work for all of them?
You need an additional as_trait function on Trait and its implementations:
trait Trait {
fn needed(&self) -> &str;
fn provided(&self) {
use_trait(self.as_trait());
}
fn as_trait(&self) -> &Trait;
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
fn as_trait(&self) -> &Trait {
self as &Trait
}
}
You can try it on the playground. (trait objects)
Enhanced version of #JoshuaEntrekin's answer:
The helper as_trait function can be put in an auxiliary trait that gets blanket implementation for all Sized types trying to implement Trait. Then the implementer of Trait does not have to do anything special and the conversion works.
fn use_trait(x: &Trait) {
println!("object says {}", x.needed());
}
trait Trait : AsTrait {
fn needed(&self) -> &str;
fn provided(&self) where Self : AsTrait {
use_trait(self.as_trait());
}
}
trait AsTrait {
fn as_trait(&self) -> &Trait;
}
impl<T : Trait + Sized> AsTrait for T {
fn as_trait(&self) -> &Trait { self }
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
}
fn main() {
Struct().provided();
}
(on play).
It would also be possible to simply put provided in the auxiliary trait, but then it would have to dynamically dispatch to the other methods of Self unnecessarily.
Update: Actually, the point is that it should still be possible to override provided.
Now the above can be improved further by making it generic. There is std::makrer::Unsize, which is unstable at the time of this writing. We can't make
trait Trait : Unsize<Trait>
because Rust does not allow CRTP, but fortunately it is enough to put the constraint on the method. So
fn use_trait(x: &Trait) {
println!("object says {}", x.needed());
}
trait Trait {
fn needed(&self) -> &str;
fn provided(&self) where Self: AsObj<Trait> {
use_trait(self.as_obj());
}
}
trait AsObj<Tr: ?Sized> {
fn as_obj(&self) -> &Trait;
}
// For &'a Type for Sized Type
impl<Type: Trait> AsObj<Trait> for Type {
fn as_obj(&self) -> &Trait { self }
}
// For trait objects
impl AsObj<Trait> for Trait {
fn as_obj(&self) -> &Trait { self }
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
fn provided(&self) {
println!("Aber dieses Objekt sagt Grüß Gott, Welt!"); // pardon my German, it is rusty.
}
}
fn main() {
let s: &Trait = &Struct();
s.provided();
}
(on play)
This finally makes it transparent for the implementors of other versions.
See also this users thread.

Resources