Is it possible to cast a trait to the implementing struct? [duplicate] - rust

I have a collection of Trait, a function that iterates over it and does something, and then I would like to check the implementor type and if it is of type Foo then downcast it and call some Foo method.
Basically, something similar to Go's type-switch and interface conversion.
Searching around I found about the Any trait but it can only be implemented on 'static types.
To help demonstrate what I want:
let vec: Vec<Box<Trait>> = //
for e in vec.iter() {
e.trait_method();
// if typeof e == Foo {
// let f = e as Foo;
// f.foo_method();
//}
}

As you have noticed, downcasting only works with Any trait, and yes, it only supports 'static data. You can find a recent discussion on why it is so here. Basically, implementing reflection for references of arbitrary lifetimes is difficult.
It is also impossible (as of now, at least) to combine Any with your custom trait easily. However, a macro library for automatic implementation of Any for your trait has recently been created. You can also find some discussion on it here.

This isn't a Rust-specific problem, although the vocabulary may be a little different. The ideal way to solve a problem like this, not just with traits in Rust but in any language, is to add the desired behavior (foo_method in your example) to the abstract interface (Trait):
trait Trait {
fn trait_method(&self);
fn foo_method(&self) {} // does nothing by default
}
struct Foo;
impl Trait for Foo {
fn trait_method(&self) {
println!("In trait_method of Foo");
}
fn foo_method(&self) {
// override default behavior
println!("In foo_method");
}
}
struct Bar;
impl Trait for Bar {
fn trait_method(&self) {
println!("In trait_method of Bar");
}
}
fn main() {
let vec: Vec<Box<dyn Trait>> = vec![Box::new(Foo), Box::new(Bar)];
for e in &vec {
e.trait_method();
e.foo_method();
}
}
In this example, I have put a default implementation of foo_method in Trait which does nothing, so that you don't have to define it in every impl but only the one(s) where it applies. You should really attempt to make the above work before you resort to downcasting to a concrete type, which has serious drawbacks that all but erase the advantages of having trait objects in the first place.
That said, there are cases where downcasting may be necessary, and Rust does support it -- although the interface is a little clunky. You can downcast &Trait to &Foo by adding an intermediate upcast to &Any:
use std::any::Any;
trait Trait {
fn as_any(&self) -> &dyn Any;
}
struct Foo;
impl Trait for Foo {
fn as_any(&self) -> &dyn Any {
self
}
}
fn downcast<T: Trait + 'static>(this: &dyn Trait) -> Option<&T> {
this.as_any().downcast_ref()
}
as_any has to be a method in Trait because it needs access to the concrete type. Now you can attempt to call Foo methods on a Trait trait object like this (complete playground example):
if let Some(r) = downcast::<Foo>(&**e) {
r.foo_method();
}
To make this work, you have to specify what type you expect (::<Foo>) and use if let to handle what happens when the referenced object is not an instance of Foo. You can't downcast a trait object to a concrete type unless you know exactly what concrete type it is.
If you ever need to know the concrete type, trait objects are almost useless anyway! You probably should use an enum instead, so that you will get compile-time errors if you omit to handle a variant somewhere. Furthermore, you can't use Any with non-'static structs, so if any Foo might need to contain a reference, this design is a dead end. The best solution, if you can do it, is to add foo_method to the trait itself.

Related

What does this "As lifts over &" AsRef impl mean?

On debugging (step-in) this Rust code:
use std::fs;
fn main() {
fs::create_dir("my_dir").unwrap();
}
I go to an impl I feel hard to understand:
// rustup\toolchains\stable-x86_64-pc-windows-gnu\lib\rustlib\src\rust\library\core\src\convert\mod.rs
// As lifts over &
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized, U: ?Sized> AsRef<U> for &T
where
T: AsRef<U>,
{
fn as_ref(&self) -> &U {
<T as AsRef<U>>::as_ref(*self)
}
}
Which then calls:
#[stable(feature = "rust1", since = "1.0.0")]
impl AsRef<Path> for str {
#[inline]
fn as_ref(&self) -> &Path {
Path::new(self)
}
}
What does this "As lifts over &" impl mean? Why it is needed?
This piece of code:
<T as AsRef<U>>
I've never seen this syntax before. What does it mean?
Seems like there is a lot of implicit conversions like this done by Rust compiler behind our daily Rust code. Is there any way to learn them besides debugging?
I assume As lifts over & simply means that if T can be taken as a reference to U, so does &T, because why not?
It also gives some convenience, e.g. if you happened to have a value of type &&&&&&T, you won't need to (******t).as_ref(), it's just t.as_ref(), because by using such implementation, as_ref goes through all levels of references up to the T object itself and takes it as a reference to U.
How can you learn it? Well, the top 3 most popular Rust textbooks have it in some way (not necessarily explicitly explaining you this particular case, but giving you enough knowledge to understand them implicitly. So I advice you to read one (and read it more carefully).
About the <T as AsRef<U>>, it is definitely covered in the books. It's needed to disambiguate calls to functions. For example, if some type implements two different traits, but both traits have the same method as_ref, then you'll need to fully qualify function of which trait you mean to call, for this you <T as AsRef<U>> and it's immediately clear which as_ref function you call.
Lets say you have 2 traits that share an item and a type that implements both. You can use this syntax to specify which implementation to use.
pub trait Foo {
const BAZ: usize;
}
pub trait Bar {
const BAZ: usize;
}
pub fn thing<T: Foo + Bar>() {
// This wont compile since it doesn't know which BAZ to use
println!("Unknown: {}", T::BAZ);
// You can use <a as b>::c to further specify which to use
println!("As Foo: {}", <T as Foo>::BAZ);
println!("As Bar: {}", <T as Bar>::BAZ);
}
You can read <A as B>::C as follows where C can be anything inside a trait (constant, function, type, etc.). It can also be used to make your code more verbose. If it is unclear which trait is being used, this can help make your code more explicit.
"Interpret type A as an instance of trait B for the purpose of retrieving C"

Differences between 2 styles of default implementations in a trait?

There are 2 ways to provide methods for a Trait itself, Rustdoc distinguishes them by saying "provided methods" and impl dyn XXX. For example:
trait Trait {
fn foo(&self) {
println!("Default implementation");
}
}
impl Trait {
fn bar(&self) {
println!("Anonymous implementation?");
}
}
I noticed it when I was reading the documentation of Rust's failure crate.
What are the use cases for them? What are the differences?
The first snippet,
trait Trait {
fn foo(&self) {
println!("Default implementation");
}
}
implements a provided method on the trait. This method can be overridden by a trait implementation, but it does not have to be overridden.
The second snippet,
impl Trait {
fn bar(&self) {
println!("Anonymous implementation?");
}
}
implements an inherent method on a trait object of type dyn Trait. Method implementations for dyn Trait can only be called for trait objects, e.g. of type &dyn Trait. They can't receive self by value, since dyn Trait does not have a size known at compile time, and they can't be called on concrete types implementing Trait (including generic types with a Trait bound).
The modern notation is to write impl dyn Trait instead of impl Trait, and in fact this notation was one of the motivating examples for the introduction of the dyn keyword – the old syntax did not provide any clues as to what the semantics are, whereas the new syntax with the dyn keyword hints at the fact that this impl is only used together with dynamic dispatch.
A trait object is a fat pointer to an object implementing Trait, but the concrete type of the object is not necessarily known at compile time. The fat pointer contains a pointer to the object data, as well as a pointer to the virtual method table of the object type. The latter is used to dynamically dispatch to the correct trait implementation at runtime.
It is rather uncommon to use impl dyn Trait. Generally it's only useful if you want to make use of some dynamic type information, like downcasting to the actual type. The only traits with inherent methods on trait objects in the standard library are Any and Error.
In short: one can be overridden, and the other cannot.
When you define a trait, you define items that implementations of the trait may (or have to) override:
trait Trait {
fn foo(&self) {
println!("Default implementation");
}
}
impl Trait for i64 {
fn foo(&self) {
println!("i64 implementation: {}", self);
}
}
On the other hand, using impl Trait, you define inherent methods, which cannot be overridden:
impl Trait {
fn bar(&self) {
self.foo();
self.foo()
}
}
// Try:
impl Trait for i64 {
fn bar(&self) { ... } // error: bar cannot be overridden.
}
As a result, inherent traits methods act as the Template Method Pattern: they provide a canvas linking together one or multiple overridable method(s).
If you look at the failure crate that you linked, the method Failure::find_root_cause() states:
This is equivalent to iterating over iter_causes() and taking the last item.
You may consider those inherent methods to be convenience methods, methods providing an easy/intuitive interface for common tasks which can be accomplished manually... but are conveniently pre-defined.
Note: any inherent method could be implemented as a free function taking the trait as a first argument; however free functions cannot be called in method position.

Is it possible to get back the implementing type from a trait? [duplicate]

I have a collection of Trait, a function that iterates over it and does something, and then I would like to check the implementor type and if it is of type Foo then downcast it and call some Foo method.
Basically, something similar to Go's type-switch and interface conversion.
Searching around I found about the Any trait but it can only be implemented on 'static types.
To help demonstrate what I want:
let vec: Vec<Box<Trait>> = //
for e in vec.iter() {
e.trait_method();
// if typeof e == Foo {
// let f = e as Foo;
// f.foo_method();
//}
}
As you have noticed, downcasting only works with Any trait, and yes, it only supports 'static data. You can find a recent discussion on why it is so here. Basically, implementing reflection for references of arbitrary lifetimes is difficult.
It is also impossible (as of now, at least) to combine Any with your custom trait easily. However, a macro library for automatic implementation of Any for your trait has recently been created. You can also find some discussion on it here.
This isn't a Rust-specific problem, although the vocabulary may be a little different. The ideal way to solve a problem like this, not just with traits in Rust but in any language, is to add the desired behavior (foo_method in your example) to the abstract interface (Trait):
trait Trait {
fn trait_method(&self);
fn foo_method(&self) {} // does nothing by default
}
struct Foo;
impl Trait for Foo {
fn trait_method(&self) {
println!("In trait_method of Foo");
}
fn foo_method(&self) {
// override default behavior
println!("In foo_method");
}
}
struct Bar;
impl Trait for Bar {
fn trait_method(&self) {
println!("In trait_method of Bar");
}
}
fn main() {
let vec: Vec<Box<dyn Trait>> = vec![Box::new(Foo), Box::new(Bar)];
for e in &vec {
e.trait_method();
e.foo_method();
}
}
In this example, I have put a default implementation of foo_method in Trait which does nothing, so that you don't have to define it in every impl but only the one(s) where it applies. You should really attempt to make the above work before you resort to downcasting to a concrete type, which has serious drawbacks that all but erase the advantages of having trait objects in the first place.
That said, there are cases where downcasting may be necessary, and Rust does support it -- although the interface is a little clunky. You can downcast &Trait to &Foo by adding an intermediate upcast to &Any:
use std::any::Any;
trait Trait {
fn as_any(&self) -> &dyn Any;
}
struct Foo;
impl Trait for Foo {
fn as_any(&self) -> &dyn Any {
self
}
}
fn downcast<T: Trait + 'static>(this: &dyn Trait) -> Option<&T> {
this.as_any().downcast_ref()
}
as_any has to be a method in Trait because it needs access to the concrete type. Now you can attempt to call Foo methods on a Trait trait object like this (complete playground example):
if let Some(r) = downcast::<Foo>(&**e) {
r.foo_method();
}
To make this work, you have to specify what type you expect (::<Foo>) and use if let to handle what happens when the referenced object is not an instance of Foo. You can't downcast a trait object to a concrete type unless you know exactly what concrete type it is.
If you ever need to know the concrete type, trait objects are almost useless anyway! You probably should use an enum instead, so that you will get compile-time errors if you omit to handle a variant somewhere. Furthermore, you can't use Any with non-'static structs, so if any Foo might need to contain a reference, this design is a dead end. The best solution, if you can do it, is to add foo_method to the trait itself.

How do I use unsized types/traits in Rust? [duplicate]

This question already has answers here:
What is the correct way to return an Iterator (or any other trait)?
(2 answers)
Closed 6 years ago.
I have the following
trait T {}
type Iter = fn() -> Iterator<Item = T>;
fn func(iter: Iter) {
for a in iter() {
// ...
}
}
I would like iter to return an Iterator with move semantics, so I shouldn't have to return &Iterator. Problem is, Iterator is a trait, so it's unsized. The above code gets a compile error saying that Iterable does not satisfy the Sized trait because all local variables have to be statically sized.
On top of this, T is also a trait, therefore unsized, so I can't bind a to it either because it's unsized.
I'm new to Rust so this is really tripping me up. How do I use unsized types?
You probably shouldn't use unsized types at all here. Use generics instead:
trait Foo {} // T is a common name for type parameters, so use a different name
fn func<I, F>(iter: F) where I: Iterator, I::Item: Foo, F: FnOnce() -> I {
for a in iter() {
// ...
}
}
Check out the book chapters on generics and Fn* traits.
And by the way, accepting a function doesn't seem very useful, since you just call it once at the start. It seems much simpler to write the function like this:
fn func<I>(iter: I) where I: Iterator, I::Item: Foo {
for a in iter {
// ...
}
}
Unsized types only "work" if there is an additional level of indirection involved. Example:
trait Ttait {}
fn foo(x: Trait) {} // error: x would be unsized which is not allowed
fn bar(x: &Trait) {} // OK: x is just a reference
fn baz(x: Box<Trait>) {} // OK: x is just an "owning pointer"
But instead of using traits as types, you should probably prefer to use traits as bounds for type parameters of generic functions and structs:
fn generic<Type: Trait>(x: Type) {}
This is actually not a function but a family of functions. For each type Type that implements Trait the compiler will create a special version of generic if needed. If you have some value x of a concrete type that implements Trait and write generic(x) you will be invoking a function that is created especially for that type which is unsized. This is called "monomorphization".
So, traits have two uses. They act as unsized types (for trait objects, dynamic dispatch, dynamic polymorphism) as well as type bounds for generic code (static dispatch, static polymorphism). My suggestion would be to avoid trait objects if you can and prefer generic code.
Without knowing exactly what you're trying to do, let me show you one possibility:
trait Trait {
fn foo(&self);
}
fn func<'x,I>(iterable: I) where I: IntoIterator<Item = &'x Trait> {
for a in iterable {
a.foo();
}
}
struct Example;
impl Trait for Example {
fn foo(&self) {
println!("Example::foo()");
}
}
fn main() {
let arr = [Example, Example];
func(arr.iter().map(|x| x as &Trait));
}
This is an example which demonstrates both kinds of trait uses. func is generic over the kinds of iterables it takes but uses dynamic dispatch to invoke the right foo function. This is probably closest to what you were trying to do.
You could also go "full generic" (avoiding the dynamic dispatch for the foo calls. See delnan's answer) or "full dynamic" (making func agnostic about what kind of iterator it actually deals with at runtime). With more context we can probably make better suggestions.

Can I cast between two traits?

Is there a way to cast from one trait to another?
I have the traits Foo and Bar and a Vec<Box<dyn Foo>>. I know some of the items in the Vec implement the Bar trait, but is there any way I could target them?
I don't understand if this is possible or not.
trait Foo {
fn do_foo(&self);
}
trait Bar {
fn do_bar(&self);
}
struct SomeFoo;
impl Foo for SomeFoo {
fn do_foo(&self) {
println!("doing foo");
}
}
struct SomeFooBar;
impl Foo for SomeFooBar {
fn do_foo(&self) {
println!("doing foo");
}
}
impl Bar for SomeFooBar {
fn do_bar(&self) {
println!("doing bar");
}
}
fn main() {
let foos: Vec<Box<dyn Foo>> = vec![Box::new(SomeFoo), Box::new(SomeFooBar)];
for foo in foos {
foo.do_foo();
// if let Some(val) = foo.downcast_whatever::<Bar>() {
// val.bar();
// }
}
}
[Playground](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=8b637bddc4fc923ce705e84ad1d783d4)
No. There is no way to cast between two unrelated traits. To understand why, we have to understand how trait objects are implemented. To start with, let's look at TraitObject.
TraitObject is a reflection of how trait objects are actually implemented. They are composed of two pointers: data and vtable. The data value is just a reference to the original object:
#![feature(raw)]
use std::{mem, raw};
trait Foo {}
impl Foo for u8 {}
fn main() {
let i = 42u8;
let t = &i as &dyn Foo;
let to: raw::TraitObject = unsafe { mem::transmute(t) };
println!("{:p}", to.data);
println!("{:p}", &i);
}
vtable points to a table of function pointers. This table contains references to each implemented trait method, ordered by some compiler-internal manner.
For this hypothetical input
trait Foo {
fn one(&self);
}
impl Foo for u8 {
fn one(&self) { println!("u8!") }
}
The table is something like this pseudocode
const FOO_U8_VTABLE: _ = [impl_of_foo_u8_one];
A trait object knows a pointer to the data and a pointer to a list of methods that make up that trait. From this information, there is no way to get any other piece of data.
Well, almost no way. As you might guess, you can add a method to the vtable that returns a different trait object. In computer science, all problems can be solved by adding another layer of indirection (except too many layers of indirection).
See also:
Why doesn't Rust support trait object upcasting?
But couldn't the data part of the TraitObject be transmuted to the struct
Not safely, no. A trait object contains no information about the original type. All it has is a raw pointer containing an address in memory. You could unsafely transmute it to a &Foo or a &u8 or a &(), but neither the compiler nor the runtime data have any idea what concrete type it originally was.
The Any trait actually does this by also tracking the type ID of the original struct. If you ask for a reference to the correct type, the trait will transmute the data pointer for you.
Is there a pattern other than the one I described with my FooOrBar trait to handle such cases where we need to iterate over a bunch of trait objects but treat some of them slightly different?
If you own these traits, then you can add as_foo to the Bar trait and vice versa.
You could create an enum that holds either a Box<dyn Foo> or a Box<dyn Bar> and then pattern match.
You could move the body of bar into the body of foo for that implementation.
You could implement a third trait Quux where calling <FooStruct as Quux>::quux calls Foo::foo and calling <BarStruct as Quux>::quux calls Bar::foo followed by Bar::bar.
so... I don't think this is exactly what you want, but it's the closest I can get.
// first indirection: trait objects
let sf: Box<Foo> = Box::new(SomeFoo);
let sb: Box<Bar> = Box::new(SomeFooBar);
// second level of indirection: Box<Any> (Any in this case
// is the first Box with the trait object, so we have a Box<Box<Foo>>
let foos: Vec<Box<Any>> = vec![Box::new(sf), Box::new(sb)];
// downcasting to the trait objects
for foo in foos {
match foo.downcast::<Box<Foo>>() {
Ok(f) => f.do_foo(),
Err(other) => {
if let Ok(bar) = other.downcast::<Box<Bar>>() {
bar.do_bar();
}
}
}
}
note that we can call SomeFooBar as a Box<Bar> only because we stored it as a Box<Bar> in the first place. So this is still not what you want (SomeFooBar is a Foo too, but you can't convert it to a Box<Foo> any longer, so we're not really converting one trait to the other)
The short answer is: there is extremely limited support for downcasting at the moment in the language.
The long answer is that being able to downcast is not seen as high-priority for both technical and philosophical reasons:
from a technical stand-point, there are workarounds for most if not all situations
from a philosophical stand-point, downcasting leads to more brittle software (as you unexpectedly start relying on implementation details)
There have been multiple proposals, and I myself participated, but for now none has been selected and it is unclear whether Rust will ever get downcasting or if it does what its limitations will be.
In the mean time, you have essentially two workarounds:
Use TypeId: each type has an associated TypeId value which can be queried, then you can build a type-erased container such as Any and query whether the type it holds is a specific X. Behind the scenes Any will simply check the TypeId of this X against the TypeId of the value stored.
Create a specific trait, as you did.
The latter is more open-ended, and notably can be used with traits, whereas the former is limited to concrete types.
Here is what I did.
I added an as_bar method to the Foo trait that returns an Option<&Bar>. I gave the trait a default implementation to return None so that there is little to no inconvenience for Foo implementers that don't bother about Bar.
trait Foo {
fn do_foo(&self);
fn as_bar(&self) -> Option<&dyn Bar> {
None
}
}
I overwrite that method for the SomeFooBar struct which implements both Foo and Bar to return Some(self):
impl Foo for SomeFooBar {
fn do_foo(&self) {
println!("doing foo");
}
fn as_bar(&self) -> Option<&dyn Bar> {
Some(self)
}
}
Which makes the calling code look pretty much the way I want it to look.
fn main() {
let foos: Vec<Box<dyn Foo>> = vec![Box::new(SomeFoo), Box::new(SomeFooBar)];
for foo in foos {
foo.do_foo();
if let Some(bar) = foo.as_bar() {
bar.do_bar();
}
}
}
Playground
I would love to see Rust improve on this part in the future, but it's a solution I can totally live with for my case.
The only solution that I found originally is to introduce a third trait FooOrBar with explicit converter methods and implement that for both types. It doesn't feel like the right tool for the job though.
trait FooOrBar {
fn to_bar(&self) -> Option<&dyn Bar>;
fn to_foo(&self) -> Option<&dyn Foo>;
}
impl FooOrBar for SomeFooBar {
fn to_bar(&self) -> Option<&dyn Bar> {
Some(self)
}
fn to_foo(&self) -> Option<&dyn Foo> {
None
}
}
impl FooOrBar for SomeFoo {
fn to_bar(&self) -> Option<&dyn Bar> {
None
}
fn to_foo(&self) -> Option<&dyn Foo> {
Some(self)
}
}
fn main() {
let foos: Vec<Box<dyn FooOrBar>> = vec![Box::new(SomeFoo), Box::new(SomeFooBar)];
for foo in foos {
foo.to_foo().map(|foo| foo.do_foo());
foo.to_bar().map(|foo| foo.do_bar());
}
}

Resources