How do I get around supertrait computation cycle? [duplicate] - rust

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"
}
}

Related

Can not implement io::Read for a custom trait

I want to implement the std::io::Read trait for my custom trait. This is my try:-
use std::io::Read;
trait Bar {
fn foo(&self);
}
impl<T: Bar> Read for T {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
Ok(0)
}
}
Playground
Error:-
Compiling playground v0.0.1 (/playground)
error[E0119]: conflicting implementations of trait `std::io::Read` for type `&mut _`:
--> src/lib.rs:7:1
|
7 | impl<T: Bar> Read for T {
| ^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `std`:
- impl<R> std::io::Read for &mut R
where R: std::io::Read, R: ?Sized;
= note: downstream crates may implement trait `Bar` for type `&mut _`
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
--> src/lib.rs:7:6
|
7 | impl<T: Bar> Read for T {
| ^ type parameter `T` must be used as the type parameter for some local type
|
= note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
= note: only traits defined in the current crate can be implemented for a type parameter
error: aborting due to 2 previous errors
This is currently not allowed, as explained in the note:
= note: downstream crates may implement trait `Bar` for type `&mut _`
You are trying to implement Read for all types that implement Bar, but you can't guarantee that all implementations of Bar (present or future) don't - or won't - implement Read already. For example, a user of your crate might implement Bar for one of their own types.
Another problem is explained in the second error:
= note: implementing a foreign trait is only possible if at least one of the types for which is it implemented is local
= note: only traits defined in the current crate can be implemented for a type parameter
You are only allowed to implement a trait if either the trait is defined in your crate (Read is not) or if the type that you are implementing it for is defined in your crate. That means you can't implement Read for T, because T is literally any type, including arbitrary types from other crates.
A workaround for this would be to add a function that exposes a Read implementation from an arbitrary T: Bar:
use std::io::Read;
trait Bar {
fn foo(&self);
fn as_reader(&self) -> BarRead<&Self> {
BarRead(self)
}
}
pub struct BarRead<T>(T);
impl<T: Bar> Read for BarRead<T> {
fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
Ok(0)
}
}

Rust trait bound by supertrait with itself as generic parameter

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

Compiler forces me to implement trait method but the `Self` trait bound on method is never satisfied for my type

I have a trait Foo. I want to force implementors to define a method, if those implementors implement another trait (Clone in this example). My idea (Playground):
trait Foo {
// Note: in my real application, the trait has other methods as well,
// so I can't simply add `Clone` as super trait
fn foo(&self)
where
Self: Clone;
}
struct NoClone;
impl Foo for NoClone {}
Sadly, this leads to:
error[E0046]: not all trait items implemented, missing: `foo`
--> src/lib.rs:8:1
|
2 | / fn foo(&self)
3 | | where
4 | | Self: Clone;
| |____________________- `foo` from trait
...
8 | impl Foo for NoClone {}
| ^^^^^^^^^^^^^^^^^^^^ missing `foo` in implementation
I don't understand this error: the compiler clearly knows that NoClone does not implement Clone, so why am I required to provide a definitoin for foo? In particular, if I attempt to provide a definition (Playground):
impl Foo for NoClone {
fn foo(&self)
where
Self: Clone
{
unreachable!()
}
}
I get the error:
error[E0277]: the trait bound `NoClone: std::clone::Clone` is not satisfied
--> src/lib.rs:9:5
|
9 | / fn foo(&self)
10 | | where
11 | | Self: Clone
12 | | {
13 | | unreachable!()
14 | | }
| |_____^ the trait `std::clone::Clone` is not implemented for `NoClone`
|
= help: see issue #48214
= help: add #![feature(trivial_bounds)] to the crate attributes to enable
So the compiler knows for sure. (FYI: with #![feature(trivial_bounds)] it compiles, but I don't want to define a bunch of methods with unreachable!() as body.)
Why does the compiler force me to provide the method definition? Can I work around this problem somehow?
All implementors of a trait need to implement all methods that don't have a default implementation. It's the point of a trait that it has a defined interface. Adding trait bounds to a method does not change anything about this rule.
This is what the language reference says on the topic:
A trait implementation must define all non-default associated items declared by the implemented trait, may redefine default associated items defined by the implemented trait, and cannot define any other items.
This also means that a trait bound on Self in a method declaration on a trait is functionally equivalent to declaring a supertrait, except that the trait can only be used in the method that declares the bound.
The obvious work-around is to define a separate trait for the methods that have additional requirements on Self:
trait FooExt: Foo + Clone {
fn foo(&self);
}
You can now implement Foo for all types, and FooExt in addition for the types that are Clone.
Updated as requested in the comments: There is a GitHub issue discussing whether it should be allowed to implement methods with unsatisfiable trait bounds without the method body, so at least the { unimplemted()! } part could be dropped. As of April 2019, this discussion has not come to any conclusion yet, and not even the exact syntax for implementing the uncallable methods has been settled.

Blanket implementation of core traits constrained by locally-defined public trait

I have the following code:
pub trait GetIdentifier {
//...
}
impl<T: GetIdentifier> Hash for T {
fn hash(&self) -> //....
}
I get the following error:
error[E0119]: conflicting implementations of trait `std::hash::Hash` for type `&_`:
--> <anon>:18:1
|
18 | / impl<T: GetIdentifier> Hash for T {
19 | | }
| |_^
|
= note: conflicting implementation in crate `core`
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g. `MyStruct<T>`); only traits defined in the current crate can be implemented for a type parameter
--> <anon>:18:1
|
18 | / impl<T: GetIdentifier> Hash for T {
19 | | }
| |_^
Why? I haven't implemented GetIdentifier for &_, so the blanket impl shouldn't apply to &_. Consumers of my crate wouldn't be able to implement GetIdentifier for core types either, so no problem there. What am I missing here? Why is &_ even involved here -- I didn't put an ?Sized bound on my trait so references shouldn't even be considered....right?
Consumers of your crate might implement GetIdentifier for TheirType and simultaneously implement Hash for TheirType.
Now you might say that's their problem, but imagine another crate with a trait Foo that also does impl<T: Foo> Hash for T {}, and TheirType implementing Foo and GetIdentifier. Suddenly they can't implement either trait.
The reason the error occurs for &_ is that the stdlib impl says any type T implementing Hash causes &T to also implement Hash.

How do I bound a trait with a supertrait that uses the trait's associated type as a parameter?

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"
}
}

Resources