I have code similar to the one below (playground):
trait SomeTrait {
fn some_fn(&self) -> i32;
}
#[derive(Debug, Clone, Eq, PartialEq)]
enum Foo {
Bar,
Baz(Box<dyn SomeTrait>),
}
fn main() {
// Do something with Foo
}
I have a trait object being held in Foo::Baz and I want whatever to be in there to have Debug, Clone, and Eq derived. The above code won't compile and you'll get an error saying that the trait object doesn't implement the derived traits. I get why it doesn't compile. But I want to know how I can solve this problem. In my actual project, all the derived traits are necessary so simply removing them isn't an option. I thought about using super traits but when you try to write something like:
trait SomeTrait: DynClone + std::fmt::Debug + Eq + PartialEq {
fn some_fn(&self) -> i32;
}
This would require the struct that SomeTrait is being implemented for to have already derived all of the required traits so that it can be used in Foo. Unfortunately now I get an error saying that the trait cannot be made into an object, and I don't understand what it means by that. Can anybody help me figure out how to tackle this problem?
The error means that not every trait can be made into a trait object. Here is more detailed explanation. You can not use Self in generic parameters of such traits while all Clone, Eq and PartialEq do use Self. When using dyn (dynamic dispatch) there isn't enough information to know what Self is and therefore what type of reference the methods of Clone, Eq or PartialEq can accept(explained here and here).
Why don't you go with static dispatch instead of dynamic dispath? Then everything works and you have improved performance at runtime
use std::fmt::Debug;
trait SomeTrait{
fn some_fn(&self) -> i32;
}
#[derive(Debug, Clone, Eq, PartialEq)]
enum Foo<T:SomeTrait> {
Bar,
Baz(T),
}
fn main() {
// Do something with Foo
}
Related
In this question:
How to get a reference to a concrete type from a trait object?
Explains how to use Any to downcast a Trait. But I would like to downcast directly from the Trait to all the supported known types instead of using a generic as_any method. I tried:
pub trait A{
fn as_B(&self) -> B {
(&self as &dyn Any).downcast_ref::<B>().expect("Can't cast to B").clone()
}
}
impl A for B{}
But I get the error: lifetime may not live long enough cast requires that '1 must outlive 'static
I have tried:
Mark trait A as Clone, but this is not an option since this break other Traits that uses A and need those traits to be object traits.
Put life time parameters in the trait. This litter a lot of places where my trait A is used and in the end I get the same compilation error.
Remove & from self. But is neither an option since A size is not known.
Edited:
If you just want what Any does without going through it here rust playground, this is UB since Any is meant to translate to usable type,
trait A{
// Edited from std::any::Any downcast_ref_unchecked there is a checked version if you want to look
fn downcast_ref_unchecked(&self) -> &B where Self: Sized{
// Don't ask me how this works but I believe it dips into C land of pointers
unsafe { &*(self as *const dyn A as *const B) }
}
}
// Target
#[derive(Debug)]
struct B;
// Test
#[derive(Debug)]
struct C;
// Have test struct have 'A' trait
impl A for C {}
fn main(){
let t = C{};
println!("{:?}", t.downcast_ref_unchecked());
println!("{:?}", t);
}
I want trait implementations in Rust to be able to return arbitrary iterators (of specific item type) that may reference the original object with a lifetime 'a without having to explicitly mention 'a in the trait generics and everywhere where the trait is used or otherwise introducing significant trait bound bloat to user code. The only simple way I've figured to do this is that the trait has to be implemented for &'a MyStruct instead of MyStruct (this approach is used in some places in the standard library), but the significant drawback is that in generic code wrappers cannot “own” implementations of the trait (MyStruct) without exposing the lifetime in trait bounds all over the code. So nothing gained when ownership is needed.
Another way I figured out that should work (just done the simple test below so far) is to use higher-ranked lifetime bounds on a “base trait” that would implement the iterator-generation functions. In the code below Foo is the main trait, FooInterfaceGen is the iterator-generator trait that has its lifetime “hidden” through for <'a> when assumed as a super-trait of Foo. The FooInterface generated by FooInterfaceGen would be the trait for an appropriate type of iterator when modified to that application of the idea. However, it seems impossible to make additional trait bounds on the specific implementation FooInterfaceGen::Interface. The code below works, but when you uncomment the Asdf trait bound in the function footest, the compiler complains that
the trait `for<'a> Asdf` is not implemented for `<_ as FooInterfaceGen<'a>>::Interface
But I have implemented Asdf! It's as if the compiler is ignoring the 'a in the expression <T as FooInterfaceGen<'a>> and just applying for<'a> to the right-hand-side. Any ideas if this is a compiler bug, a known restriction, or of any ways around it?
trait FooInterface<'a> {
fn foo(&self) -> u32;
}
trait FooInterfaceGen<'a> {
type Interface : FooInterface<'a>;
fn gen(&'a self) -> Self::Interface;
}
trait Foo : for<'a> FooInterfaceGen<'a> { }
struct S2;
struct S1(S2);
impl<'a> FooInterfaceGen<'a> for S1 {
type Interface = &'a S2;
fn gen(&'a self) -> Self::Interface { &self.0 }
}
impl Foo for S1 { }
impl<'a> FooInterface<'a> for &'a S2 {
fn foo(&self) -> u32 { 42 }
}
trait Asdf {}
impl<'a> Asdf for &'a S2 {}
fn footest<T : Foo>(a : &T) -> u32
/* where for<'a> <T as FooInterfaceGen<'a>>::Interface : Asdf */ {
a.gen().foo()
}
fn main() {
let q = S1(S2);
println!("{}", footest(&q));
}
(Regarding some alternative implementations, maybe there's a technical reason for it, but otherwise I really don't understand the reason behind the significant trait bound bloat that Rust code easily introduces. Assuming a trait should in any reasonable situation automatically assume all the trait bound as well, also in generic code, not just specific code, without having to copy-paste an increasing number of where-clauses all over the code.)
The error seems to be a known compiler bug: https://github.com/rust-lang/rust/issues/89196
I'm trying to extend the Iterator trait with a custom function:
pub trait CustomTrait<T> : Iterator<Item=T> where Self: Sized, Self: 'static {
#[inline]
fn custom_function(self) -> Result<Box<dyn Iterator>, Err> {
let boxed = Box::new(self);
do_something(boxed)
}
}
It's compiles correctly. However, when I go to call my new trait on a iterator, the compiler complains about it not being implemented for the iterator. (I have tried std::ops::Range and std::slice::Iter<'_, {integer}>.)
I was able to get each function call working for individual struct types with lines such as below:
impl CustomTrait<u64> for std::ops::Range<u64> {}
However, I assume there must be a way that I can get my trait definition to apply to all Iterables without having to manually implement it for each struct that implements the Iterable trait.
When you extend a trait, you basically create a new trait that also guarantees that it has all the methods defined by the trait it's extending.
What it not does is automatically implement any of those methods for the trait you're extending.
In addition to what you already did, you actually need to implement your trait for Iterator:
impl<T> CustomTrait<T> for Iterator<Item=T> {
...
}
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.
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.