What does it mean to implement a trait for another trait? - rust

I read this answer but I'm still confused.
How do you interpret impl B for dyn A {}?
trait A {
fn method_a(&self) {
println!("a");
}
}
trait B {
fn method_b(&self) {
println!("b")
}
}
impl B for dyn A {}
impl A for i32 {}
fn main() {
let x: &dyn A = &10;
x.method_b();
}
Playground
I can understand impl A for i32 {} because i32 is a concrete type. dyn A is not a concrete type (unsized, can't pass by value), and you cannot declare a dyn A but you can only declare a &dyn A. Should I interpret
// x.method_b();
(*x).method_b();
as *x is dyn A?
I can also declare impl B for &dyn A {}, so why I need impl B for dyn A {}? What's the use case?
Follow up: If I modify the code
fn main() {
let x: &dyn A = &10;
// have a B trait object over dyn A since
// dyn A implements B
let y: &dyn B = x;
}
It will fail and complain &dyn A is not &dyn B. I understand this is a reasonable complain but I provide the option for compiler to use impl B for dyn A {}. Apparently, the compiler doesn't consider that's an option.

You can't declare dyn A but you can declare &dyn A because dyn A is a trait object type while &dyn A is a pointer to an instance of type T that implements A.
Historically, a trait could be used as a type and a trait. For instance, these both work:
// Where B and A are traits
impl B for A {}
impl B for dyn A {}
Basically, dyn A is just a sugar over A to make it clearer that it is meant to be used as a trait object type. You don't implement a trait for another trait. You implement a trait for another trait object type.
&dyn A is a pointer instance to an instance of type T that implements A and a virtual method table (vtable), which contains all the baggage of methods of A that T implements. This vtable lookup is necessary when an instance of type T later calls A's implementation at runtime.
Therefore, dyn A is an unsized type while &dyn A is a pointer with a known size.
Trait object of type dyn A must be cast from a pointer to be used as a concrete type that implements A. For example, in the code example, i32 can be cast to a dyn A:
impl B for dyn A {}
impl A for i32 {}
fn main() {
let x: i32 = 10;
(&x as &dyn A).method_a();
(&x as &dyn A).method_b();
}
or it can be coerced by a function:
fn dispatch(a: &dyn A) {
a.method_b();
}
Because traits are dynamically sized types (DSTs), to use them as trait objects, we must put them behind some kind of pointer, like &dyn A or Box<dyn A> so it can point to a variable-sized value and access the vtable to call the implemented methods.
See also: What makes something a “trait object”?

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).

Downcasting a Box<dyn ForeignTrait> inside a struct?

I'm having some dificulty wrapping my head around downcasting.
I have a struct which contains a Boxed trait, for example:
struct MyContainer {
reader: Box<dyn std::io::Read>,
}
How could I downcast the reader to my known type?
Here is a minimal example of what I'm trying to do:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2015&gist=14b44be2412342d851d3c83fd9e080b0
Normally, I'd create a as_any(&self) on the trait which would result in a &dyn Any but this is a foreign trait which I do not own.
Define your own trait which has both Any and Read as supertraits, then use that as the dyn type.
trait DowncastableRead: Read + Any {
fn as_any(&self) -> &dyn Any;
}
impl<T: Read + Any> DowncastableRead for T {
fn as_any(&self) -> &dyn Any {
self
}
}
struct MyContainer {
reader: Box<dyn DowncastableRead>,
}
With these changes, your desired code will run.
There's no way to avoid defining your own trait, because dyn requires specifying exactly one non-auto trait (carries one vtable pointer), to get the functionality of Read and the functionality of Any, you need to define a trait that combines them (and defines a vtable that supports both sets of methods).

How can I convert dyn FnMut into a custom trait object? [duplicate]

In the code below it is not possible to obtain a reference to a trait object from a reference to a dynamically-sized type implementing the same trait. Why is this the case? What exactly is the difference between &dyn Trait and &(?Sized + Trait) if I can use both to call trait methods?
A type implementing FooTraitContainerTrait might e.g. have type Contained = dyn FooTrait or type Contained = T where T is a concrete type that implements FooTrait. In both cases it's trivial to obtain a &dyn FooTrait. I can't think of another case where this wouldn't work. Why isn't this possible in the generic case of FooTraitContainerTrait?
trait FooTrait {
fn foo(&self) -> f64;
}
///
trait FooTraitContainerTrait {
type Contained: ?Sized + FooTrait;
fn get_ref(&self) -> &Self::Contained;
}
///
fn foo_dyn(dyn_some_foo: &dyn FooTrait) -> f64 {
dyn_some_foo.foo()
}
fn foo_generic<T: ?Sized + FooTrait>(some_foo: &T) -> f64 {
some_foo.foo()
}
///
fn foo_on_container<C: FooTraitContainerTrait>(containing_a_foo: &C) -> f64 {
let some_foo = containing_a_foo.get_ref();
// Following line doesn't work:
//foo_dyn(some_foo)
// Following line works:
//some_foo.foo()
// As does this:
foo_generic(some_foo)
}
Uncommenting the foo_dyn(some_foo) line results in the compiler error
error[E0277]: the size for values of type `<C as FooTraitContainerTrait>::Contained` cannot be known at compilation time
--> src/main.rs:27:22
|
27 | foo_dyn(contained)
| ^^^^^^^^^ doesn't have a size known at compile-time
|
= help: the trait `std::marker::Sized` is not implemented for `<C as FooTraitContainerTrait>::Contained`
= note: to learn more, visit <https://doc.rust-lang.org/book/ch19-04-advanced-types.html#dynamically-sized-types-and-the-sized-trait>
= help: consider adding a `where <C as FooTraitContainerTrait>::Contained: std::marker::Sized` bound
= note: required for the cast to the object type `dyn FooTrait`
This problem can be reduced to the following simple example (thanks to turbulencetoo):
trait Foo {}
fn make_dyn<T: Foo + ?Sized>(arg: &T) -> &dyn Foo {
arg
}
At first glance, it really looks like this should compile, as you observed:
If T is Sized, the compiler knows statically what vtable it should use to create the trait object;
If T is dyn Foo, the vtable pointer is part of the reference and can just be copied to the output.
But there's a third possibility that throws a wrench in the works:
If T is some unsized type that is not dyn Foo, even though the trait is object safe, there is no vtable for impl Foo for T.
The reason there is no vtable is because the vtable for a concrete type assumes that self pointers are thin pointers. When you call a method on a dyn Trait object, the vtable pointer is used to look up a function pointer, and only the data pointer is passed to the function.
However, suppose you implement a(n object-safe) trait for an unsized type:
trait Bar {}
trait Foo {
fn foo(&self);
}
impl Foo for dyn Bar {
fn foo(&self) {/* self is a fat pointer here */}
}
If there were a vtable for this impl, it would have to accept fat pointers, because the impl may use methods of Bar which are dynamically dispatched on self.
This causes two problems:
There's nowhere to store the Bar vtable pointer in a &dyn Foo object, which is only two pointers in size (the data pointer and the Foo vtable pointer).
Even if you had both pointers, you can't mix and match "fat pointer" vtables with "thin pointer" vtables, because they must be called in different ways.
Therefore, even though dyn Bar implements Foo, it is not possible to turn a &dyn Bar into a &dyn Foo.
Although slices (the other kind of unsized type) are not implemented using vtables, pointers to them are still fat, so the same limitation applies to impl Foo for [i32].
In some cases, you can use CoerceUnsized (only on nightly as of Rust 1.36) to express bounds like "must be coercible to &dyn FooTrait". Unfortunately, I don't see how to apply this in your case.
See also
What is a "fat pointer" in Rust?
Use trait object to pass str in rust has a concrete example of a reference to an unsized type (str) that cannot be coerced to a reference to a trait object.
Not sure if that solves your concrete problem, but I did solve mine with the following trick:
I added the following method to FooTrait:
fn as_dyn(&self) -> &dyn FooTrait;
A default impl can not be provided (because it requires that Self be Sized, but constraining FooTrait to be Sized forbids creating trait objects for it...).
However, for all Sized implementations, it is trivially implemented as
fn as_dyn(&self) -> &dyn FooTrait { self }
So basically it constrains all implementations of FooTrait to be sized, except for dyn FooTrait.
Try it on the playground
Referenced from this blog, which explains the fat pointer really well.
Thanks trentcl for simplifying the question to:
trait Foo {}
fn make_dyn<T: Foo + ?Sized>(arg: &T) -> &dyn Foo {
arg
}
This brings to how to cast between different ?Sized?
To answer this, let's first peek the implementation for Unsized type Trait.
trait Bar {
fn bar_method(&self) {
println!("this is bar");
}
}
trait Foo: Bar {
fn foo_method(&self) {
println!("this is foo");
}
}
impl Bar for u8 {}
impl Foo for u8 {}
fn main() {
let x: u8 = 35;
let foo: &dyn Foo = &x;
// can I do
// let bar: &dyn Bar = foo;
}
So, can you do let bar: &dyn Bar = foo;?
// below is all pseudo code
pub struct TraitObjectFoo {
data: *mut (),
vtable_ptr: &VTableFoo,
}
pub struct VTableFoo {
layout: Layout,
// destructor
drop_in_place: unsafe fn(*mut ()),
// methods shown in deterministic order
foo_method: fn(*mut ()),
bar_method: fn(*mut ()),
}
// fields contains Foo and Bar method addresses for u8 implementation
static VTABLE_FOO_FOR_U8: VTableFoo = VTableFoo { ... };
From the pseudo code, we can know
// let foo: &dyn Foo = &x;
let foo = TraitObjectFoo {&x, &VTABLE_FOO_FOR_U8};
// let bar: &dyn Bar = foo;
// C++ syntax for contructor
let bar = TraitObjectBar(TraitObjectFoo {&x, &VTABLE_FOO_FOR_U8});
The bar type is TraitObjectBar, which is not the type TraitObjectFoo. That is to say, you cannot assign a struct of one type to another different type (in rust, in C++ you can use reinterpret_cast).
What you can do it to have another level of indirection:
impl Bar for dyn Foo {
...
}
let bar: &dyn Bar = &foo;
// TraitObjectFoo {&foo, &VTABLE_FOO_FOR_DYN_FOO}
The same thing applies to Slice.
The workaround for casting different Unsized can be done by this trick:
// blanket impl for all sized types, this allows for a very large majority of use-cases
impl<T: Bar> AsBar for T {
fn as_bar(&self) -> &dyn Bar { self }
}
// a helper-trait to do the conversion
trait AsBar {
fn as_bar(&self) -> &dyn Bar;
}
// note that Bar requires `AsBar`, this is what allows you to call `as_bar`
// from a trait object of something that requires `Bar` as a super-trait
trait Bar: AsBar {
fn bar_method(&self) {
println!("this is bar");
}
}
// no change here
trait Foo: Bar {
fn foo_method(&self) {
println!("this is foo");
}
}

Why can you only specify type restrictions with traits?

In Rust you can only use traits to specify type restrictions, for example: <T: A + B>, A and B must be traits, they cannot be primitive types like i32 or f64.
trait Foo {}
impl Foo for i32 {}
fn blah<T: i32>(val: T) {
// ^^^ works if this is Foo
println!("hello\n");
}
fn main() {
let toast: i32 = 33;
blah(toast);
}
You can get around this by implementing a trait for a primitive, but why would the designers of the language not let you use primitives as a restriction?
Because specifying type restrictions with types does not make sense.
The very reason for using generic programming is to allow multiple concrete types. This is why you express bounds, and any type that satisfy those bounds can be used.
If you wish to use a concrete type, you certainly can... but not with a generic function; you just use a regular function.
Note that bounds are additives, that is, when specifying T: X + Y you are not saying that any type implementing EITHER X OR Y is expected, but that you want a type that implements BOTH X AND Y. The + here is an intersection.
As a result:
T: i32 is useless, just specify i32 as the type
T: i32 + f32 is non-sensical, no type is BOTH a i32 and a f32 at the same type
So... how do you specify the OR relationship? By using a trait!
trait MyTrait: std::fmt::Display {}
impl MyTrait for i32 {}
impl MyTrait for f32 {}
fn hello<T: MyTrait>(t: &T) {
println!("{:?}", t);
}
Note: other people may implement your trait for their own types if it is public.

How do I specify lifetime parameters in an associated type?

I have this trait and simple structure:
use std::path::{Path, PathBuf};
trait Foo {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(&self) -> Self::Iter;
}
struct Bar {
v: Vec<PathBuf>,
}
I would like to implement the Foo trait for Bar:
impl Foo for Bar {
type Item = PathBuf;
type Iter = std::slice::Iter<PathBuf>;
fn get(&self) -> Self::Iter {
self.v.iter()
}
}
However I'm getting this error:
error[E0106]: missing lifetime specifier
--> src/main.rs:16:17
|
16 | type Iter = std::slice::Iter<PathBuf>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ expected lifetime parameter
I found no way to specify lifetimes inside that associated type. In particular I want to express that the iterator cannot outlive the self lifetime.
How do I have to modify the Foo trait, or the Bar trait implementation, to make this work?
Rust playground
There are a two solutions to your problem. Let's start with the simplest one:
Add a lifetime to your trait
trait Foo<'a> {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(&'a self) -> Self::Iter;
}
This requires you to annotate the lifetime everywhere you use the trait. When you implement the trait, you need to do a generic implementation:
impl<'a> Foo<'a> for Bar {
type Item = &'a PathBuf;
type Iter = std::slice::Iter<'a, PathBuf>;
fn get(&'a self) -> Self::Iter {
self.v.iter()
}
}
When you require the trait for a generic argument, you also need to make sure that any references to your trait object have the same lifetime:
fn fooget<'a, T: Foo<'a>>(foo: &'a T) {}
Implement the trait for a reference to your type
Instead of implementing the trait for your type, implement it for a reference to your type. The trait never needs to know anything about lifetimes this way.
The trait function then must take its argument by value. In your case you will implement the trait for a reference:
trait Foo {
type Item: AsRef<Path>;
type Iter: Iterator<Item = Self::Item>;
fn get(self) -> Self::Iter;
}
impl<'a> Foo for &'a Bar {
type Item = &'a PathBuf;
type Iter = std::slice::Iter<'a, PathBuf>;
fn get(self) -> Self::Iter {
self.v.iter()
}
}
Your fooget function now simply becomes
fn fooget<T: Foo>(foo: T) {}
The problem with this is that the fooget function doesn't know T is in reality a &Bar. When you call the get function, you are actually moving out of the foo variable. You don't move out of the object, you just move the reference. If your fooget function tries to call get twice, the function won't compile.
If you want your fooget function to only accept arguments where the Foo trait is implemented for references, you need to explicitly state this bound:
fn fooget_twice<'a, T>(foo: &'a T)
where
&'a T: Foo,
{}
The where clause makes sure that you only call this function for references where Foo was implemented for the reference instead of the type. It may also be implemented for both.
Technically, the compiler could automatically infer the lifetime in fooget_twice so you could write it as
fn fooget_twice<T>(foo: &T)
where
&T: Foo,
{}
but it's not smart enough yet.
For more complicated cases, you can use a Rust feature which is not yet implemented: Generic Associated Types (GATs). Work for that is being tracked in issue 44265.
Use a wrapper type
If the trait and all its implementations are defined in one crate, a helper type can be useful:
trait Foo {
fn get<'a>(&'a self) -> IterableFoo<'a, Self> {
IterableFoo(self)
}
}
struct IterableFoo<'a, T: ?Sized + Foo>(pub &'a T);
For a concrete type that implements Foo, implement the iterator conversion on the IterableFoo wrapping it:
impl Foo for Bar {}
impl<'a> IntoIterator for IterableFoo<'a, Bar> {
type Item = &'a PathBuf;
type IntoIter = std::slice::Iter<'a, PathBuf>;
fn into_iter(self) -> Self::IntoIter {
self.0.v.iter()
}
}
This solution does not allow implementations in a different crate. Another disadvantage is that an IntoIterator bound cannot be encoded into the definition of the trait, so it will need to be specified as an additional (and higher-rank) bound for generic code that wants to iterate over the result of Foo::get:
fn use_foo_get<T>(foo: &T)
where
T: Foo,
for<'a> IterableFoo<'a, T>: IntoIterator,
for<'a> <IterableFoo<'a, T> as IntoIterator>::Item: AsRef<Path>
{
for p in foo.get() {
println!("{}", p.as_ref().to_string_lossy());
}
}
Associated type for an internal object providing desired functionality
The trait can define an associated type that gives access to a part of the object that, bound in a reference, provides the necessary access traits.
trait Foo {
type Iterable: ?Sized;
fn get(&self) -> &Self::Iterable;
}
This requires that any implementation type contains a part that can be so exposed:
impl Foo for Bar {
type Iterable = [PathBuf];
fn get(&self) -> &Self::Iterable {
&self.v
}
}
Put bounds on the reference to the associated type in generic code that uses the the result of get:
fn use_foo_get<'a, T>(foo: &'a T)
where
T: Foo,
&'a T::Iterable: IntoIterator,
<&'a T::Iterable as IntoIterator>::Item: AsRef<Path>
{
for p in foo.get() {
println!("{}", p.as_ref().to_string_lossy());
}
}
This solution permits implementations outside of the trait definition crate.
The bound work at generic use sites is as annoying as with the previous solution.
An implementing type may need an internal shell struct with the only purpose of providing the associated type, in case when the use-site bounds are not as readily satisfied as with Vec and IntoIterator in the example discussed.
In future, you'll want an associated type constructor for your lifetime 'a but Rust does not support that yet. See RFC 1598

Resources