When working with a trait hierarchy, I got into the following problem: A trait (that is used as a trait object) should define a function that returns its own type. Of course, Self cannot be used since it would be different for each subtype. Instead, the trait should return the type of the trait object, like:
trait MySpecialTrait {
fn clone(&self) -> Box<dyn MySpecialTrait>;
}
So far, this works. But now I want to extract this functionality in its own generic supertrait to be able to write generic code that only relies on the supertrait properties, like this:
trait Cloneable<Output: ?Sized> {
fn clone(&self) -> Box<Output>;
}
trait MySpecialTrait: Cloneable<dyn MySpecialTrait> {
fn something(&self);
}
fn clone_all<T>(data: &Vec<Box<T>>) -> Vec<Box<T>>
where T: ?Sized + Cloneable<T>
{
data.iter().map(|x|x.clone()).collect()
}
But this code yields the following error:
error[E0391]: cycle detected when computing the supertraits of `MySpecialTrait`
--> src\main.rs:28:1
|
28 | trait MySpecialTrait: Cloneable<dyn MySpecialTrait> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: ...which again requires computing the supertraits of `MySpecialTrait`, completing the cycle
note: cycle used when collecting item types in top-level module
--> src\main.rs:28:1
|
28 | trait MySpecialTrait: Cloneable<dyn MySpecialTrait> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Using associated types like this does not change anything:
trait Cloneable {
type Output;
fn clone(&self) -> Box<Self::Output>;
}
trait MySpecialTrait: Cloneable<Output = dyn MySpecialTrait> {
fn something(&self);
}
Can someone explain to me why there is a cycle? I understand that in the general case of trait A: B the supertraits of B have to be known to calculate the supertraits of A, so
trait A: B {}
trait B: A {}
cannot work. But in this case, MySpecialTrait is only referenced in the generic type arguments, so why is it required to compute the supertraits of Cloneable<dyn MySpecialTrait>?
Apart from that, is there are workaround for this problem?
Edit
I do not want clone_all to work only with all concrete subtypes of Cloneable<T>, but also with all trait types, e.g. I want to call clone_all with an argument of type Vec<Box<dyn MySpecialTrait>>.
Related
I'm trying to implement a trait that has a supertrait, like so:
trait A {
fn do_a(&self);
}
trait B: A {
fn do_b(&self);
}
struct S {}
impl B for S {
fn do_a(&self) {}
fn do_b(&self) {}
}
When I run cargo build I get these compiler errors:
error[E0407]: method `do_a` is not a member of trait `B`
--> src/example.rs:12:5
|
12 | fn do_a(&self) {}
| ^^^^^^^^^^^^^^^^^ not a member of trait `B`
error[E0277]: the trait bound `example::S: example::A` is not satisfied
--> src/example.rs:11:6
|
5 | trait B: A {
| - required by this bound in `example::B`
...
11 | impl B for S {
| ^ the trait `example::A` is not implemented for `example::S`
I keep re-reading about supertraits, but I'm having trouble understanding this error.
The first error seems to contradict what trait B: A is telling the program to do.
The second error seems to contradict that the implementation of do_a(&self) {} satisfies A.
What am I missing here? Is my mental model wrong, or is my code missing some necessary boilerplate, or both?
I believe that the syntax is more about bounding the type (e.g. a type T that implements B must necessarily implement A) than inheritance in the object-oriented sense. If you write out the impls separately it compiles fine:
trait A {
fn do_a(&self);
}
trait B: A {
fn do_b(&self);
}
struct S {}
impl A for S {
fn do_a(&self) {}
}
impl B for S {
fn do_b(&self) {}
}
Playground
N.B. that if you remove the impl for A the code no longer compiles, the constraint that an impl of B must also impl A is no longer satisfied.
I have a trait Trait with an associated type Trait::Associated. I am trying to bound the trait by requiring that it be indexable by its associated type, as shown here:
use std::ops::Index;
pub trait Trait: Index<Trait::Associated> {
type Associated;
}
However, the compiler complains that the associated type is ambiguous
error[E0223]: ambiguous associated type
--> src/main.rs:3:24
|
3 | pub trait Trait: Index<Trait::Associated> {
| ^^^^^^^^^^^^^^^^^ ambiguous associated type
|
= note: specify the type using the syntax `<Type as Trait>::Associated`
I also tried referring to the associated type as Self::Associated, but then the compiler protests about a cyclic reference between the type and the trait:
error[E0391]: cyclic dependency detected
--> src/main.rs:3:1
|
3 | pub trait Trait: Index<Self::Associated> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic reference
|
note: the cycle begins when computing the supertraits of `Trait`...
--> src/main.rs:3:1
|
3 | pub trait Trait: Index<Self::Associated> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which then again requires computing the supertraits of `Trait`, completing the cycle.
Finally, I also tried explicitly implementing Index for Trait:
pub trait Trait {
type Associated;
}
impl<T: Trait> Index<T::Associated> for T {
type Output = str;
fn index(&self, associated: T::Associated) -> &'static str {
"sup"
}
}
Unfortunately that fails too:
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g. `MyStruct<T>`)
--> src/main.rs:7:1
|
7 | impl<T: Trait> Index<T::Associated> for T {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type parameter `T` must be used as the type parameter for some local type
|
= note: only traits defined in the current crate can be implemented for a type parameter
Am I trying to do something unreasonable here? Is there a way of achieving something similar, without having to use generics?
Playground.
You are close, very close.
The Trait does not assume that any reference to Trait in its definition refers to the current type. After all, you could wish to refer to other types also implementing Trait.
In order to specify that you want a specific type, you should heed the compilers note: use <Type as Trait>::Associated, where Type is the current type.
When defining the Trait, how do you refer to the concrete type for which it will be instantiated? You use Self!
The solution is:
pub trait Trait: Index<<Self as Trait>::Associated> {
type Associated;
}
I think the following provides the semantics you want, taking the approach of your second attempt.
pub trait Trait {
type Associated;
}
impl<T> Index<T> for Trait<Associated = T> {
type Output = str;
fn index(&self, associated: T) -> &'static str {
"sup"
}
}
I have a trait Trait with an associated type Trait::Associated. I am trying to bound the trait by requiring that it be indexable by its associated type, as shown here:
use std::ops::Index;
pub trait Trait: Index<Trait::Associated> {
type Associated;
}
However, the compiler complains that the associated type is ambiguous
error[E0223]: ambiguous associated type
--> src/main.rs:3:24
|
3 | pub trait Trait: Index<Trait::Associated> {
| ^^^^^^^^^^^^^^^^^ ambiguous associated type
|
= note: specify the type using the syntax `<Type as Trait>::Associated`
I also tried referring to the associated type as Self::Associated, but then the compiler protests about a cyclic reference between the type and the trait:
error[E0391]: cyclic dependency detected
--> src/main.rs:3:1
|
3 | pub trait Trait: Index<Self::Associated> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cyclic reference
|
note: the cycle begins when computing the supertraits of `Trait`...
--> src/main.rs:3:1
|
3 | pub trait Trait: Index<Self::Associated> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: ...which then again requires computing the supertraits of `Trait`, completing the cycle.
Finally, I also tried explicitly implementing Index for Trait:
pub trait Trait {
type Associated;
}
impl<T: Trait> Index<T::Associated> for T {
type Output = str;
fn index(&self, associated: T::Associated) -> &'static str {
"sup"
}
}
Unfortunately that fails too:
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g. `MyStruct<T>`)
--> src/main.rs:7:1
|
7 | impl<T: Trait> Index<T::Associated> for T {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type parameter `T` must be used as the type parameter for some local type
|
= note: only traits defined in the current crate can be implemented for a type parameter
Am I trying to do something unreasonable here? Is there a way of achieving something similar, without having to use generics?
Playground.
You are close, very close.
The Trait does not assume that any reference to Trait in its definition refers to the current type. After all, you could wish to refer to other types also implementing Trait.
In order to specify that you want a specific type, you should heed the compilers note: use <Type as Trait>::Associated, where Type is the current type.
When defining the Trait, how do you refer to the concrete type for which it will be instantiated? You use Self!
The solution is:
pub trait Trait: Index<<Self as Trait>::Associated> {
type Associated;
}
I think the following provides the semantics you want, taking the approach of your second attempt.
pub trait Trait {
type Associated;
}
impl<T> Index<T> for Trait<Associated = T> {
type Output = str;
fn index(&self, associated: T) -> &'static str {
"sup"
}
}
I am trying to use a trait that has a function that takes a closure as argument, and then use it on a trait object.
trait A {
fn f<P>(&self, p: P) where P: Fn() -> ();
}
struct B {
a: Box<A>
}
impl B {
fn c(&self) {
self.a.f(|| {});
}
}
This snippet generates the following error:
the trait `A` is not implemented for the type `A` [E0277]
The version of rustc is rustc 1.0.0-beta.3 (5241bf9c3 2015-04-25) (built 2015-04-25).
The problem is that method f is not object-safe because it is generic, and hence it can't be called on a trait object. You will have to force its users to pass boxed closure:
trait A {
fn f(&self, p: Box<Fn() -> ()>);
}
I wonder why Rust allows Box<A> in the first place, I would expect an error there. And this particular error is really misleading. I would file a bug about this.
Alternatively, you can discard trait objects in favor of regular bounded generics, though it is not always possible.
If I have a trait, and a function that accepts a generic type constrained to that type, everything works fine. If I try to pass in a reference to that type, I get a compilation error.
trait Trait {
fn hello(&self) -> u32;
}
struct Struct(u32);
impl Trait for Struct {
fn hello(&self) -> u32 {
self.0
}
}
fn runner<T: Trait>(t: T) {
println!("{}", t.hello())
}
fn main() {
let s = Struct(42);
// Works
runner(s);
// Doesn't work
runner(&s);
}
error[E0277]: the trait bound `&Struct: Trait` is not satisfied
--> src/main.rs:24:5
|
24 | runner(&s);
| ^^^^^^ the trait `Trait` is not implemented for `&Struct`
|
= help: the following implementations were found:
<Struct as Trait>
note: required by `runner`
--> src/main.rs:13:1
|
13 | fn runner<T: Trait>(t: T) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^
I can fix the issue by implementing the trait for any reference to a type that implements the trait:
impl<'a, T> Trait for &'a T
where
T: Trait,
{
fn hello(&self) -> u32 {
(*self).hello()
}
}
The piece of information that I'm missing is when shouldn't I implement this? Asked another way, why doesn't the compiler automatically implement this for me? Since it currently doesn't, I assume there must be cases where having this implementation would be disadvantageous.
when shouldn't I implement this? Asked another way, why doesn't the compiler automatically implement this for me? Since it currently doesn't, I assume there must be cases where having this implementation would be disadvantageous.
As an example, the Default trait immediately came to mind.
pub trait Default {
fn default() -> Self;
}
I could implement it for T, but there is no way to automatically implement it for &T.
The particular trait you are writing here only takes self by reference, and that is the only reason it is possible to write the additional implementation you did.
For this reason, taking the parameter to runner() by value is probably undesirable; you should instead be taking it by reference. This guideline can apply generally: if it is possible to implement the trait for a reference then rather than wondering “should I implement it?” you should wonder “why would I implement it?” for the only cases where you would use it should probably be altered to take the object by reference in the first place.