Rust Trait object conversion - rust

The following code won't compile due to two instances of this error:
error[E0277]: the trait bound Self: std::marker::Sized is not satisfied
I don't understand why Sized is required in this instance as both &self and &Any are pointers and the operation does not require knowledge of the size of the structure that implements the trait, it only requires knowledge of the pointer itself and the type it is converting from and to, which it will have because &self is generic when implemented inside a trait.
I think this may be an instance of the compiler enforcing unnecessary constraints and I've considered filing an issue with the rust-lang GitHub repo but I figured I should probably see if someone here knows something I don't before I go file an issue.
use std::any::Any;
trait Component: Any {
fn as_any(&self) -> &Any {
self
}
fn as_any_mut(&mut self) -> &mut Any {
self
}
}
The alternative to this is to make as_any() and as_any_mut() required functions for the structs that implement this trait, but for those structures the implementation would always be exactly as displayed here down to each individual character, resulting in several instances of identical boilerplate code.

Dynamically sized types can also implement traits. In particular, when you define an object-safe trait, the compiler also defines a dynamically sized type with the same name as the trait, which lets you use object types such as &Component.
Object types such as &Component or &Any are not just ordinary pointers; they're fat pointers. A fat pointer combines a pointer to the data and another piece of data: for object types, it's a pointer to the vtable; for slices, it's the length of the slice.
When casting from a regular pointer (e.g. a &Button) to an object type, the compiler statically knows which vtable to put in the fat pointer (e.g. Button's vtable for Any). On the other hand, Rust doesn't support casting from an object type to another object type (e.g. from &Component to &Any), because there's not enough data in an object to initialize the new fat pointer. This is why the compiler adds this note to the error message:
= note: required for the cast to the object type `std::any::Any + 'static`
There are two ways to fix this:
Require that all types implementing Component be Sized:
trait Component: Any + Sized {
fn as_any(&self) -> &Any {
self
}
fn as_any_mut(&mut self) -> &mut Any {
self
}
}
This has the consequence that you will not be able to use object types such as &Component or Box<Component> at all.
Make the as_any and as_any_mut methods only available when Self is Sized:
trait Component: Any {
fn as_any(&self) -> &Any
where Self: Sized
{
self
}
fn as_any_mut(&mut self) -> &mut Any
where Self: Sized
{
self
}
}
This way, you can still use object types for the trait, but you will not be able to call as_any and as_any_mut on them.

I found what I consider to be a great solution that didn't require new compiler features.
pub trait Component {
// ...
}
pub trait ComponentAny: Component + Any {
fn as_any(&self) -> &Any;
fn as_any_mut(&mut self) -> &mut Any;
}
impl<T> ComponentAny for T
where T: Component + Any
{
fn as_any(&self) -> &Any {
self
}
fn as_any_mut(&mut self) -> &mut Any {
self
}
}
From here, I just change all my APIs to accept ComponentAny instead of Component. Because Any is automatically implemented for any 'static type, ComponentAny is now automatically implemented for any 'static type that implements Component. Thanks to Is there a way to combine multiple traits in order to define a new trait? for the idea.

Related

Understanding "the trait X cannot be made into an object" for `&mut Box<Self>` parameter

I've got this code snippet (playground):
struct TeddyBear {
fluffiness: u8,
}
trait Scruffy {
fn scruff_up(self: &mut Box<Self>) -> Box<dyn Scruffy>;
}
impl Scruffy for TeddyBear {
fn scruff_up(self: &mut Box<Self>) -> Box<dyn Scruffy> {
// do something about the TeddyBear's fluffiness
}
}
It doesn't compile. The error is:
the trait Scruffy cannot be made into an object
, along with the hint:
because method scruff_up's self parameter cannot be dispatched on.
I checked the "E0038" error description, but I haven't been able to figure out which category my error falls into.
I also read the "object-safety" entry in "The Rust Reference", and I believe this matches the "All associated functions must either be dispatchable from a trait object", but I'm not sure, partly because I'm not sure what "receiver" means in that context.
Can you please clarify for me what's the problem with this code and why it doesn't work?
The problem is when you pass it in as a reference, because the inner type may not be well-sized (e.g. a trait object, like if you passed in a Box<Fluffy>) the compiler doesn't have enough information to figure out how to call methods on it. If you restrict it to sized objects (like your TeddyBear) it should compile
trait Scruffy {
fn scruff_up(self: &mut Box<Self>) -> Box<dyn Scruffy> where Self: Sized;
}
A receiver is the self (&self, &mut self, self: &mut Box<Self> and so on).
Note that the list you cited from the reference lists both Box<Self> and &mut Self, but does not list &mut Box<Self> nor it says that combinations of these types are allowed.
This is, indeed, forbidden. As for the why, it is a little more complex.
In order for a type to be a valid receiver, it needs to hold the following condition:
Given any type Self that implements Trait and the receiver type Receiver, the receiver type should implement DispatchFromDyn<dyn Trait> for itself with all Self occurrences of Self replaced with dyn Trait.
For instance:
&self (has the type &Self) has to implement DispatchFromDyn<&dyn Trait>, which it does.
Box<Self> has to implement DispatchFromDyn<Box<dyn Trait>>, which it does.
But in order for &mut Box<Self> to be an object-safe receiver, it would need to impl DispatchFromDyn<&mut Box<dyn Trait>>. What you want is kind of blanket implementation DispatchFromDyn<&mut T> for &mut U where U: DispatchFromDyn<T>.
This impl will never exist. Because it is unsound (even ignoring coherence problems).
As explained in the code in rustc that calculates this:
The only case where the receiver is not dispatchable, but is still a valid receiver type (just not object-safe), is when there is more than one level of pointer indirection. E.g., self: &&Self, self: &Rc<Self>, self: Box<Box<Self>>. In these cases, there is no way, or at least no inexpensive way, to coerce the receiver from the version where Self = dyn Trait to the version where Self = T, where T is the unknown erased type contained by the trait object, because the object that needs to be coerced is behind a pointer.
The problem is inherent to how Rust handles dyn Trait.
dyn Trait is a fat pointer: it is actually two words sized. One is a pointer to the data, and the other is a pointer to the vtable.
When you call a method on dyn Trait, the compiler looks up in the vtable, find the method for the concrete type (which is unknown at compilation time, but known at runtime), and calls it.
This all may be very abstract without an example:
trait Trait {
fn foo(&self);
}
impl Trait for () {
fn foo(&self) {}
}
fn call_foo(v: &dyn Trait) {
v.foo();
}
fn create_dyn_trait(v: &impl Trait) {
let v: &dyn Trait = v;
call_foo(v);
}
The compiler generates code like:
trait Trait {
fn foo(&self);
}
impl Trait for () {
fn foo(&self) {}
}
struct TraitVTable {
foo: fn(*const ()),
}
static TRAIT_FOR_UNIT_VTABLE: TraitVTable = TraitVTable {
foo: unsafe { std::mem::transmute(<() as Trait>::foo) },
};
type DynTraitRef = (*const (), &'static TraitVTable);
impl Trait for dyn Trait {
fn foo(self: DynTraitRef) {
(self.1.foo)(self.0)
}
}
fn call_foo(v: DynTraitRef) {
v.foo();
}
fn create_dyn_trait(v: &impl Trait) {
let v: DynTraitRef = (v as *const (), &TRAIT_FOR_UNIT_VTABLE);
call_foo(v);
}
Now suppose that the pointer to the value is behind an indirection. I'll use Box<&self> because it's simple but demonstrates the concept best, but the concept applies to &mut Box<Self> too: they have the same layout. How will we write foo() for impl Trait for dyn Trait?
trait Trait {
fn foo(self: Box<&Self>);
}
impl Trait for () {
fn foo(self: Box<&Self>) {}
}
struct TraitVTable {
foo: fn(Box<*const ()>),
}
static TRAIT_FOR_UNIT_VTABLE: TraitVTable = TraitVTable {
foo: unsafe { std::mem::transmute(<() as Trait>::foo) },
};
type DynTraitRef = (*const (), &'static TraitVTable);
impl Trait for dyn Trait {
fn foo(self: Box<DynTraitRef>) {
let concrete_foo: fn(Box<*const ()>) = self.1.foo;
let data: *const () = self.0;
concrete_foo(data) // We need to wrap `data` in `Box`! Error.
}
}
You may think "then the compiler should just insert a call to Box::new()!" But besides Box not being the only one here (what with Rc, for example?) and we will need some trait to abstract over this behavior, Rust never performs any hard work implicitly. This is a design choice, and an important one (as opposed to e.g. C++, where an innocent-looking statement like auto v1 = v; can allocate and copy 10GB by a copy constructor). Converting a type to dyn Trait and back is done implicitly: the first one by a coercion, the second one when you call a method of the trait. Thus, the only thing that Rust does for that is attaching a VTable pointer in the first case, or discarding it in the second case. Even allowing only references (&&&Self, no need to call a method, just take the address of a temporary) exceeds that. And it can have severe implications in unexpected places, e.g. register allocation.
So, what to do? You can take &mut self or self: Box<Self>. Which one to choose depends on whether you need ownership (use Box) or not (use a reference). And anyway, &mut Box<Self> is not so useful (its only advantage over &mut T is that you can replace the box and not just its contents, but when you do that that's usually a mistake).

How do I transform a &Vec<Box<dyn Child>> into a &Vec<Box<dyn Base>> where trait Child : Base {}? [duplicate]

This question already has answers here:
Why doesn't Rust support trait object upcasting?
(5 answers)
Can I cast between two traits?
(5 answers)
How do I convert a Vec<T> to a Vec<U> without copying the vector?
(2 answers)
Closed 2 years ago.
I've tried this post using the AsBase trait but couldn't quite get to a minimal example. And since that post is a bit old, the lack of dyn sometimes gets a bit confusing.
This is what I thought I could do:
trait Entity {}
trait Part: Entity {}
trait System {
fn parts(&self) -> &Vec<Box<dyn Entity>>; // This is the specification
}
struct System32 {
parts: Vec<Box<dyn Part>>, // But this is what I have
}
impl System for System32 {
fn parts(&self) -> &Vec<Box<dyn Entity>> {
&self.parts // error: expected trait Entity, found trait Part
// I've also tried:
// &self.parts as &Vec<Box<dyn Entity>>
// error: an `as` expression can only be used to convert between
// primitive types or to coerce to a specific trait object
}
}
Is this even possible? If yes, then how can I do the type conversion?
Is this even possible?
No.
There are two issues with it: first, Rust is not an inheritance-based language, trait B: A means "B requires A" more than "B extends A".
Now although this is probably not how Rust's trait should be thought of, there are ways to perform this "upcasting" regardless, and the language may eventually include this feature (in part for multi-trait objects).
However there is a bigger issue here: you're returning an &Vec.
This means that vector has to be owned by something as you're only returning a reference to it. But a Box<dyn Entity> and a Box<dyn Part> are completely different values.
Going back to Rust not being inheritance-based, the vtable for B does not embed the vtable for A, you can't just say "this is now a pointer to an A" because it absolutely is not[0].
This means going from Box<dyn Part> to Box<dyn Entity> is a full value conversion, not an in-place reinterpretation of the value as a different type.
Which means a new Box, and a new Vec, which means you can't just return a reference to an existing Vec claiming it's the type you want, the contents of the Vec itself need to change.
[0] unlike C++ I believe, at least for SI cases, with MI you'd have one of the parents be a simple reinterpretation but the others would have to offset the child pointer to get the correct vtable even if they're embedded, so I think you'd have the same issue
Masklinn above explained why you cannot do what you wanted to do.
I'm going here to try to provide an approach that would work for solving your problem using an approach similar to yours. Maybe there are better approaches though, could we see the bigger picture.
One way to achieve your pattern would be to have the type of entity your system manages be an associated type of your System trait. This enables you to write the following:
trait Entity {
fn do_something(&self) {}
}
trait Part: Entity {}
trait System {
type Entity: Entity + ?Sized;
// I renamed this function from "parts" to "entities" because it seems that's what you want it to return
fn entities(&self) -> &Vec<Box<Self::Entity>>; // This is the specification
fn do_something_with_entities(&self) {
for entity in self.entities() {
entity.do_something();
}
}
}
struct System32 {
parts: Vec<Box<dyn Part>>, // But this is what I have
}
impl System for System32 {
type Entity = dyn Part;
fn entities(&self) -> &Vec<Box<Self::Entity>> {
&self.parts
}
}
Now if you reaally need to be able to return some form of collection that outputs only actual dyn Entity because e.g. your system manages different types of what would be InnerEntity... well given the amount of dirty tricks I had to use I strongly beleive that is not idiomatic Rust, and you should probably express the whole problem so people can suggest more idiomatic solutions than trying to do inheritance in Rust, but in any case, this compilation of dirty stuff that eventually provides a reasonable interface with zero allocations was pretty fun to write.
// ------ TRAIT SYSTEM
// From https://users.rust-lang.org/t/casting-traitobject-to-super-trait/33524/16
// This intermediate trait allows to work around Sized/?Sized requirements
pub trait IntoSuper<Super: ?Sized> {
fn as_super(&self) -> &Super;
fn as_super_mut(&mut self) -> &mut Super;
fn into_super(self: Box<Self>) -> Box<Super>;
}
trait Entity {}
impl<'a, T: 'a + Entity> IntoSuper<dyn Entity + 'a> for T {
fn as_super(&self) -> &(dyn Entity + 'a) {
self
}
fn as_super_mut(&mut self) -> &mut (dyn Entity + 'a) {
self
}
fn into_super(self: Box<Self>) -> Box<dyn Entity + 'a> {
self
}
}
trait Part: Entity + IntoSuper<dyn Entity> {}
// The 'r lifetime needs to be at the trait level because GATs are not stable yet
// https://github.com/rust-lang/rust/issues/44265
// This workaround somewhat simulates GATs
trait System: for<'r> SystemInner<'r> {}
impl<T: for<'r> SystemInner<'r>> System for T {}
trait SystemInner<'r> {
/// Clone should be inexpensive
type EntitiesIter: Iterator<Item = &'r dyn Entity> + ExactSizeIterator + Clone + 'r;
fn entities(&'r self) -> Self::EntitiesIter; // This is the specification
}
type EntitiesIter<'r, Source> =
std::iter::Map<std::slice::Iter<'r, Box<Source>>, fn(&'r Box<Source>) -> &'r dyn Entity>;
// ------ END OF TRAIT SYSTEM
struct System32 {
parts: Vec<Box<dyn Part>>, // And this is what you have stored
}
impl<'r> SystemInner<'r> for System32 {
type EntitiesIter = EntitiesIter<'r, dyn Part>;
fn entities(&'r self) -> Self::EntitiesIter {
self.parts.iter().map(|p| p.as_super())
}
}
// System32 implements System because it implements SystemInner<'r> for every 'r
struct SomePart;
impl Entity for SomePart {}
impl Part for SomePart {}
fn main() {
let system = System32 {
parts: vec![Box::new(SomePart), Box::new(SomePart)],
};
let second_part: &dyn Entity = system.entities().nth(1).expect("We put two parts in our vec");
for part_as_dyn_entity in system.entities() {
// do stuff
}
}

How can I add extension methods to a trait with associated types lying in a different crate?

I'm trying to add an extension method to a trait in a different crate.
This trait has an associated type specified on it.
pub trait Test<W> {
type Error;
fn do_sth(&mut self) -> Result<W, Self::Error>;
}
Why is it not possible to add a method that is using the associated type Error?
impl dyn Test<u8> {
fn use_do_sth(&mut self) -> Result<u8: Self::Error> {
self.do_sth()
}
}
playground
When you need to add a method to the external type, the only option is to use extension traits. It means that you define your own trait, with whatever methods you need, and implement it for the types you need.
When you need to add a method to all types implementing some external trait, you can use the same pattern, but instead of listing the types directly, just use the trait bound:
use std::fmt::Debug;
// This is an extension trait.
// You can force all its implementors to implement also some external trait,
// so that two trait bounds essentially collapse into one.
trait HelperTrait: Debug {
fn helper_method(&mut self);
}
// And this is the "blanket" implementation,
// covering all the types necessary.
impl<T> HelperTrait for T where T: Debug {
fn helper_method(&mut self) {
println!("{:?}", self);
}
}
Playground
The same idea could be applied to any external trait, as you wish.
Do you want the following?
impl<E> dyn Test<u8, Error = E> {
fn use_do_sth(&mut self) -> Result<u8, E> {
self.do_sth()
}
}
I came up with this following the compiler's hint that "the value of the associated type Error must be specified".

Using callbacks on trait objects

I’m trying to use a callback function on a trait object. I reduced my problem to the following code (playpen):
trait Caller {
fn call(&self, call: fn(&Caller)) where Self: Sized {
call(self)
}
}
struct Type;
impl Caller for Type {}
fn callme(_: &Caller) {}
fn main() {
let caller: Box<Caller> = Box::new(Type);
caller.call(callme); // does not work
//callme(&*caller); // works
}
which results in
<anon>:14:12: 14:24 error: the trait `core::marker::Sized` is not implemented for the type `Caller` [E0277]
<anon>:14 caller.call(callme); // does not work
^~~~~~~~~~~~
Adding a Sized bound to Caller results in:
<anon>:3:14: 3:18 error: cannot convert to a trait object because trait `Caller` is not object-safe [E0038]
I really don’t understand why I need the Sized bound on the trait. Funnily it works if I use the callback directly. How do I get this to work?
Edit: Thanks to the answer I now came up with a nice solution
trait Caller {
fn borrow(&self) -> &Caller;
fn call(&self, call: fn(&Caller)) {
call(self.borrow())
}
}
struct Type;
impl Caller for Type {
fn borrow(&self) -> &Caller { self }
}
fn callme(_: &Caller) {}
fn main() {
let caller: Box<Caller> = Box::new(Type);
caller.call(callme);
}
The argument call in fn call takes a trait object &Caller, so calling it requires coercing the self reference (of type &Self) to a &Caller trait object. The coercion is only possible when &Self is a thin pointer rather than a fat pointer like a trait object or a &[T] slice. &Self is a thin pointer exactly when Self: Sized. The compiler defaults to Self in traits not being Sized, and so the extra restriction is required. The Sized trait represents that the type has a size that is known at compile time, there is no need to store extra info (next to the pointer, making it "fat") to compute it at runtime.
Unfortunately, this leaves a hole: AFAIK, it's actually not possible to have such a method be a default method and still be able to call it on trait objects, since a trait object &Caller has Self = Caller which isn't Sized. However, it should work if the method is implemented manually for each type:
trait Caller {
fn call(&self, call: fn(&Caller));
}
struct Type;
impl Caller for Type {
fn call(&self, call: fn(&Caller)) {
call(self)
}
}
fn callme(_: &Caller) {}
fn main() {
let caller: Box<Caller> = Box::new(Type);
caller.call(callme);
}
The call method declaration in the trait no longer needs the where Self: Sized since it isn't trying to do the trait object coercion itself, and the concrete implementations have much more control over how the &Caller trait object is obtained. For Sized types, it works directly, like the original where Self: Sized code.

What is 'core::kinds::Sized` is not implemented for the type `Self' in rust?

This used to work:
struct Foo<'a, T> {
parent:&'a (Array<T> + 'a)
}
impl<'a, T> Foo<'a, T> { //'
pub fn new<T>(parent:&Array<T>) -> Foo<T> {
return Foo {
parent: parent
};
}
}
trait Array<T> {
fn as_foo(&self) -> Foo<T> {
return Foo::new(self);
}
}
fn main() {
}
Now it errors:
:15:21: 15:25 error: the trait core::kinds::Sized is not implemented for the type Self
:15 return Foo::new(self);
I can kind of guess what's wrong; it's saying that my impl of Foo<'a, T> is for T, not Sized? T, but I'm not trying to store a Sized? element in it; I'm storing a reference to a Sized element in it. That should be a pointer, fixed size.
I don't see what's wrong with what I'm doing, or why it's wrong?
For example, I should (I think...) be able to store a &Array in my Foo, no problem. I can't see any reason this would force my Foo instance to be unsized.
playpen link: http://is.gd/eZSZYv
There's two things going on here: trait objects coercions (the error), and object safety (fixing it).
The error
As suggested by the error message, the difficult part of the code is the Foo::new(self), and this is because pub fn new<T>(parent: &Array<T>) -> ..., that is, self is being coerced to an &Array<T> trait object. I'll simplify the code to:
trait Array {
fn as_foo(&self) {
let _ = self as &Array; // coerce to a trait object
}
}
fn main() {}
which gives the same thing:
<anon>:3:13: 3:27 error: the trait `core::kinds::Sized` is not implemented for the type `Self`
<anon>:3 let _ = self as &Array; // coerce to a trait object
^~~~~~~~~~~~~~
Self is the stand-in name for the type that implements the trait. Unlike most generic parameters, Self is possibly-unsized (?Sized) by default, since RFC 546 and #20341 for the purposes of allowing e.g. impl Array<T> for Array<T> to work by default more often (we'll come to this later).
The variable self has type &Self. If Self is a sized type, then this is a normal reference: a single pointer. If Self is an unsized type (like [T] or a trait), then &Self (&[T] or &Trait) is a slice/trait object: a fat pointer.
The error appears because the only references &T that can be cast to a trait object are when T is sized: Rust doesn't support making fat pointers fatter, only thin pointer → fat pointer is valid. Hence, since the compiler doesn't know that Self will always be Sized (remember, it's special and ?Sized by default) it has to assume the worst: that the coercion is not legal, and so it's disallowed.
Fixing it
It seems logical that the fix we're looking for is to ensure that Self: Sized when we want to do a coercion. The obvious way to do this would be to make Self always Sized, that is, override the default ?Sized bound as follows:
trait Array: Sized {
fn as_foo(&self) {
let _ = self as &Array; // coerce to a trait object
}
}
fn main() {}
Looks good!
Except there's the small point that it doesn't work; but at least it's for a difference reason, we're making progress! Trait objects can only be made out of traits that are "object safe" (i.e. safe to be made into a trait object), and having Sized Self is one of the things that breaks object safety:
<anon>:3:13: 3:17 error: cannot convert to a trait object because trait `Array` is not object-safe [E0038]
<anon>:3 let _ = self as &Array; // coerce to a trait object
^~~~
<anon>:3:13: 3:17 note: the trait cannot require that `Self : Sized`
<anon>:3 let _ = self as &Array; // coerce to a trait object
^~~~
<anon>:3:13: 3:17 note: the trait cannot require that `Self : Sized`
<anon>:3 let _ = self as &Array; // coerce to a trait object
^~~~
(I filed the double printing of the note as #20692.)
Back to the drawing board. There's a few other "easy" possibilities for a solution:
define an extension trait trait ArrayExt: Sized + Array { fn as_foo(&self) { ... } } and implement it for all Sized + Array types
just use a free function fn array_as_foo<A: Array>(x: &A) { ... }
However, these don't necessarily work for every use case, e.g. specific types can't customise the behaviour by overloading the default method. However, fortunately there is a fix!
The Turon Trick
(Named for Aaron Turon, who discovered it.)
Using generalised where clauses we can be highly specific about when Self should implement Sized, restricting it to just the method(s) where it is required, without infecting the rest of the trait:
trait Array {
fn as_foo(&self) where Self: Sized {
let _ = self as &Array; // coerce to a trait object
}
}
fn main() {}
This compiles just fine! By using the where clause like this, the compiler understands that (a) the coercion is legal because Self is Sized so self is a thin pointer, and (b) that the method is illegal to call on a trait object anyway, and so doesn't break object safety. To see it being disallowed, changing the body of as_foo to
let x = self as &Array; // coerce to a trait object
x.as_foo();
gives
<anon>:4:7: 4:15 error: the trait `core::kinds::Sized` is not implemented for the type `Array`
<anon>:4 x.as_foo();
^~~~~~~~
as expected.
Wrapping it all up
Making this change to the original unsimplified code is as simple adding that where clause to the as_foo method:
struct Foo<'a, T> { //'
parent:&'a (Array<T> + 'a)
}
impl<'a, T> Foo<'a, T> {
pub fn new(parent:&Array<T>) -> Foo<T> {
return Foo {
parent: parent
};
}
}
trait Array<T> {
fn as_foo(&self) -> Foo<T> where Self: Sized {
return Foo::new(self);
}
}
fn main() {
}
which compiles without error. (NB. I had to remove the unnecessary <T> in pub fn new<T> because that was causing inference failures.)
(I have some in-progress blog posts that go into trait objects, object safety and the Turon trick, they will appear on /r/rust in the near future: first one.)

Resources