I'm making a tiny GUI library in Rust for practice. Minimal reproducible example of my problem is as follows:
/// Trait for rendering engines
pub trait Renderer { /* ... */ }
pub trait Element<Win: Renderer> {
fn paint(&self);
fn width(&self) -> u32;
fn height(&self) -> u32;
}
/// "Composed" element is element, that consists of multiple sub-elements, eg. button consists of frame and laconveniences a covinience trait, because implementing `Element` directly might be cumbersome.
pub trait ElComposed<Win: Renderer> {
/// Returns the top-level subelement of self
fn main_el(&self) -> Box<dyn Element<Win>>;
}
impl<T: ElComposed<Win>, Win: Renderer> Element<Win> for T {
fn paint(&self) { self.main_el().paint() }
fn width(&self) -> u32 { self.main_el().width() }
fn height(&self) -> u32 { self.main_el().height() }
}
/// Element displaying just a circle
pub struct Circle;
/// Implementing `Element` trait directly on `Circle`
impl<Win: Renderer> Element<Win> for Circle {
fn paint(&self) { todo!() }
fn width(&self) -> u32 { todo!() }
fn height(&self) -> u32 { todo!() }
}
Link to playground
Because Element trait contains quite a few methods, it may be time-consuming to implement directly, so, as you can see in the code, I added the ElComposed convenience trait to make it easier to implement for elements consisting of multiple sub-elements (eg. button consists of a frame and label).
But now, when I implement the Element trait directly (impl<Win: Renderer> Element<Win> for Circle in the code above), the code doesn't compile:
error[E0119]: conflicting implementations of trait `Element<_>` for type `Circle`
|
21 | impl<T: ElComposed<Win>, Win: Renderer> Element<Win> for T {
| ---------------------------------------------------------- first implementation here
...
31 | impl<Win: Renderer> Element<Win> for Circle {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Circle`
|
= note: downstream crates may implement trait `ElComposed<_>` for type `Circle`
But downstream crates cannot implement trait ElComposed for Circle, because both traits are in my crate. So why does this error occur and how can I fix it?
Related
I have a trait that makes sure whatever struct implements it holds specific data types so I can use those structs in any place where that trait is implemented,
trait HasShape{
fn ref_vertices(&self) -> &Vec<Vertex>;
fn ref_vbo(&self) -> &VertexBuffer<Vertex>;
// Goes on for the rest of the trait
}
trait Drawable
{
fn draw(&self, target: &Frame);
}
impl<T> Drawable for T
where T: HasShape
{
fn draw(&self, target: &Frame) {
let vertices = self.ref_vertices();
// Use the fields on type that are required by HasShape
}
}
And I have another trait that is similar however it needs to hold an object that implements HasShape as well as other fields
trait HasPos<T>
where
T: HasShape
{
fn ref_shape(&self) -> &T;
fn ref_pos(&self) -> &Vector2f32;
// Rest is the same style as HasShape
}
trait Manipulate<T>
where T: HasShape
{
fn translate(&self, x: f32, y: f32);
// Rest are similar functions that change the fields of HasPos
}
My question is, how do I implement HasShape for all types that have HasPos so I can use anything that implements HasPos in place of a HasShape object?
like this
impl<T: HasPos<T>> HasShape for T
where T: HasShape
{
fn ref_vertices(&self) -> &Vec<Vertex>{
self.ref_shape().ref_vertices()
}
// Every function in HasShape just gets the field from the object in HasPos
}
That way I can call functions from Drawable on any object that implements HasPos as well?
impl<T: HasPos<T>> HasShape for T
where T: HasShape
This doesn't make much sense; you are implementing HasShape for all types that already implement HasShape.
If I understand correctly, you want to auto-implement HasShape for all objects that already implement HasPos. If so, you actually have two generics; one for the object you want to implement HasShape for, and one for the nested type of HasPos.
The impl is a little weird with a generic that isn't actually part of the type, though, so you need to convert T of HasPos to an associated type, like so:
#![allow(unused_variables)]
use std::marker::PhantomData;
pub struct Frame;
pub struct Vertex;
pub struct VertexBuffer<B>(PhantomData<B>);
pub struct Vector2f32;
trait HasShape {
fn ref_vertices(&self) -> &Vec<Vertex>;
fn ref_vbo(&self) -> &VertexBuffer<Vertex>;
// Goes on for the rest of the trait
}
trait Drawable {
fn draw(&self, target: &Frame);
}
impl<T> Drawable for T
where
T: HasShape,
{
fn draw(&self, target: &Frame) {
let vertices = self.ref_vertices();
// Use the fields on type that are required by HasShape
}
}
trait HasPos {
type Shape: HasShape;
fn ref_shape(&self) -> &Self::Shape;
fn ref_pos(&self) -> &Vector2f32;
// Rest is the same style as HasShape
}
trait Manipulate<T>
where
T: HasShape,
{
fn translate(&self, x: f32, y: f32);
// Rest are similar functions that change the fields of HasPos
}
impl<T> HasShape for T
where
T: HasPos,
{
fn ref_vertices(&self) -> &Vec<Vertex> {
self.ref_shape().ref_vertices()
}
fn ref_vbo(&self) -> &VertexBuffer<Vertex> {
self.ref_shape().ref_vbo()
}
}
Note that there is one caveat, though: Now you can never manually implement HasShape and HasPos for the same type.
struct MyType;
impl HasPos for MyType {
type Shape = MyType;
fn ref_shape(&self) -> &Self::Shape {
todo!()
}
fn ref_pos(&self) -> &Vector2f32 {
todo!()
}
}
impl HasShape for MyType {
fn ref_vertices(&self) -> &Vec<Vertex> {
todo!()
}
fn ref_vbo(&self) -> &VertexBuffer<Vertex> {
todo!()
}
}
error[E0119]: conflicting implementations of trait `HasShape` for type `MyType`
--> src/lib.rs:73:1
|
46 | impl<T> HasShape for T
| ---------------------- first implementation here
...
73 | impl HasShape for MyType {
| ^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `MyType`
This also means that no HasShape can have itself as its HasPos type, because this will create an infinite recursion:
struct MyType;
impl HasPos for MyType {
type Shape = MyType;
fn ref_shape(&self) -> &Self::Shape {
&self
}
fn ref_pos(&self) -> &Vector2f32 {
todo!()
}
}
fn main() {
let x = MyType;
x.ref_vertices();
}
thread 'main' has overflowed its stack
fatal runtime error: stack overflow
Aborted
Basically I'm trying to make a trait that indicates the ability to be converted into a 2D ndarray aka ndarray::Array2:
trait Into2DArray{
fn to_array(&self) -> Array2<f64>;
}
I would like to do this by expanding the existing AsArray trait, but Rust forbids me from implementing a third party trait for a third party struct (polars::DataFrame) for some esoteric reason, so instead I have to make my own trait for this.
Anyway, this works well for polars::DataFrame:
impl Into2DArray for DataFrame {
fn to_array(&self) -> Array2<f64> {
return self.to_array();
}
}
However, I also want to implement this for anything that is already convertable into a 2D array, so I implement this trait for the AsArray trait mentioned above:
impl Into2DArray for AsArray<'_, f64, Ix2> {
fn to_array(&self) -> Array2<f64> {
return self.into();
}
}
However the compiler gives me grief for this:
|
26 | impl Into2DArray for AsArray<'_, f64, Ix2> {
| ^^^^^^^^^^^^^^^^^^^^^ `AsArray` cannot be made into an object
|
= note: the trait cannot be made into an object because it requires `Self: Sized`
= note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
I understand that has something to do with object safety but I thought I had fulfilled all the criteria mentioned on that page, namely the trait doesn't return Self, and all the generic parameters of AsArray are specified.
What is going wrong, and how can I fix it?
What you were trying to do is implementing the Into2DArray trait for the AsArray dynamic trait object. There should have been a warning of using AsArray without dyn anyway.
But this is not what you actually want. You want to implement it for any type that implements AsArray. Just like you did in your comment.
It is important to know the difference between these two things:
trait NeedThis {
fn can_be_called_by_the_impl(&self) {}
}
trait ToDoThis {
fn example(&self);
}
impl ToDoThis for dyn NeedThis {
fn example(&self) {
self.can_be_called_by_the_impl()
}
}
impl NeedThis for u8 {}
fn main() {
let num: u8 = 0;
// num.example(); // doesn't work because ToDoThis is not implemented for u8
let num_as_trait_obj: &dyn NeedThis = &0_u8 as &dyn NeedThis;
num_as_trait_obj.example(); // works because this time it is a trait object
}
trait NeedThis {
fn can_be_called_by_the_impl(&self) {}
}
trait ToDoThis {
fn example(&self);
}
// removing ?Sized would make it the same as T: NeedThis + Sized
impl<T: NeedThis + ?Sized> ToDoThis for T {
fn example(&self) {
self.can_be_called_by_the_impl()
}
}
impl NeedThis for u8 {}
fn main() {
let num: u8 = 0_u8;
num.example(); // works because we implemented it for all types that implement NeedThis
let num_as_trait_obj: &dyn NeedThis = &0_u8 as &dyn NeedThis;
num_as_trait_obj.example(); // works because dyn NeedThis also implements NeedThis.
// This is only true because we added ?Sized to the bounds of the impl block.
// Otherwise it doesn't work because dyn NeedThis is not actually Sized.
// And a Sized bound is implied by default.
}
In the following program, PartialOrd and PartialEq are implemented for all types that have the trait Area. This way, when defining Rectangle, I only need to implement Area to have < operator working.
trait Area {
fn get_area(&self) -> i32;
}
impl<T: Area> PartialOrd for T {
fn partial_cmp(&self, other: &T) -> Option<std::cmp::Ordering> {
self.get_area().partial_cmp(&other.get_area())
}
}
impl<T: Area> PartialEq for T {
fn eq(&self, other: &T) -> bool {
self.get_area() == other.get_area()
}
}
struct Rectangle {
width: i32,
height: i32
}
impl Area for Rectangle {
fn get_area(&self) -> i32 {
self.width * self.height
}
}
fn main() {
let r1 = Rectangle { width:10, height:10 };
let r2 = Rectangle { width:11, height:9 };
if r1 > r2 {
println!("r1 is bigger.")
} else {
println!("r2 is bigger.")
}
}
However, I am getter the following error:
error[E0119]: conflicting implementations of trait `std::cmp::PartialOrd<&_>` for type `&_`:
--> src/main.rs:23:1
|
17 | impl<T: Area> PartialOrd<T> for T {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: conflicting implementation in crate `core`:
- impl<A, B> std::cmp::PartialEq<&B> for &A
where A: std::cmp::PartialEq<B>, A: ?Sized, B: ?Sized;
= note: downstream crates may implement trait `Area` for type `&_`
The documentation for E0119 gives an example where the author writes two implementations that overlap. But there is only one implementation is this case. Even the format of the error message is strange, as it starts an enumeration of implementations (with a dash) that has only one entry. What does it mean?
I am also getting this other error:
error[E0210]: type parameter `T` must be used as the type parameter for some local type (e.g., `MyStruct<T>`)
--> src/main.rs:23:6
|
23 | impl<T: Area> PartialEq<T> 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
Again, the documentation for E0210 gives examples where the type parameter is not covered by a local type, but T is limited to Area everywhere in this case. What does it mean?
There is indeed more than one implementation in this case as pointed out in the compiler error message:
impl<A, B> std::cmp::PartialEq<&B> for &A
where A: std::cmp::PartialEq<B>, A: ?Sized, B: ?Sized;
Your impl is bound to Area. But the core library's implementation is more generic and can cover any &A including those which &A: Area.
Basically you shouldn't implement a trait foreign to your crate such as PartialOrd for any type that could be potentially foreign to your crate. Even when you are bounding them to Area they can still be defined in other crates (foreign to yours) using your crate.
It seems to me that you want to say any type which implements Area can/must implement PartialOrd and PartialEq as well and there would be a default implementation of those functions. The following solution forces the implementation of those traits and provides the default functions. Though the implementors would need to select and use the provided functions as an additional local step:
struct Rectangle {
width: i32,
height: i32
}
trait Area: PartialOrd+PartialEq {
fn get_area(&self) -> i32;
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
self.get_area().partial_cmp(&other.get_area())
}
fn eq(&self, other: &Self) -> bool {
self.get_area().eq(&other.get_area())
}
}
impl Area for Rectangle {
fn get_area(&self) -> i32 {
self.width * self.height
}
}
impl PartialOrd for Rectangle {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
<Self as Area>::partial_cmp(self, other)
}
}
impl PartialEq for Rectangle {
fn eq(&self, other: &Self) -> bool {
<Self as Area>::eq(self, other)
}
}
fn main() {
let r1 = Rectangle { width:10, height:10 };
let r2 = Rectangle { width:11, height:9 };
if r1 > r2 {
println!("r1 is bigger.")
} else {
println!("r2 is bigger.")
}
}
The rust compiler can't guarantee that another crate won't implement Area on a & type. This would cause a conflicting PartialOrd implementation. Even though in this particular example it might be impossible, the compiler can't make that judgement.
You can only implement foreign traits on concrete local types. Constraining a generic type to implement a local trait doesn't guarantee that the local trait can only be implemented on local types. If Area is later implemented on foreign type Foo, that would cause your crate to implement foreign trait PartialOrd on foreign type Foo. The common workaround for this is something like:
struct LocalArea<T: Area> {
value: T,
}
impl<T: Area, U: Area> PartialEq<LocalArea<T>> for LocalArea<U> {
...
}
This syntax of using a local struct that contains a generic T is commonly referred to as the Newtype Pattern.
I've got three examples, one using Vec one using SmallVec, and one with my own implementation of SmallVec. The ones using Vec and my own SmallVec compile but the one using the real SmallVec does not.
Working example using Vec
use std::borrow::Cow;
use std::collections::HashMap;
pub trait MyTrait {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns;
}
/// IMPORTANT PART IS HERE: `Vec<Cow<'a, str>>`
pub struct ItemTraitReturns<'a>(Vec<Cow<'a, str>>);
/// this implementation only takes items with static lifetime (but other implementations also might have different lifetimes)
pub struct MyTraitStruct {
map: HashMap<usize, ItemTraitReturns<'static>>,
}
impl MyTrait for MyTraitStruct {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns {
let temp: &ItemTraitReturns<'static> = self.map.get(&id).unwrap();
// Works as expected: I expect that I can return `&ItemTraitReturns<'_>`
// when I have `&ItemTraitReturns<'static>` (since 'static outlives everything).
temp
// Will return `&ItemTraitReturns<'_>`
}
}
Failing example with SmallVec
Uses SmallVec instead of Vec with no other changes.
use smallvec::SmallVec;
use std::borrow::Cow;
use std::collections::HashMap;
pub trait MyTrait {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns;
}
/// IMPORTANT PART IS HERE: Uses SmallVec instead of Vec
pub struct ItemTraitReturns<'a>(SmallVec<[Cow<'a, str>; 2]>);
/// this implementation only takes items with static lifetime (but other implementations also might have different lifetimes)
pub struct MyTraitStruct {
map: HashMap<usize, ItemTraitReturns<'static>>,
}
impl MyTrait for MyTraitStruct {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns {
let temp: &ItemTraitReturns<'static> = self.map.get(&id).unwrap();
temp
}
}
error[E0308]: mismatched types
--> src/lib.rs:23:9
|
23 | temp
| ^^^^ lifetime mismatch
|
= note: expected type `&ItemTraitReturns<'_>`
found type `&ItemTraitReturns<'static>`
note: the anonymous lifetime #1 defined on the method body at 18:5...
--> src/lib.rs:18:5
|
18 | / fn get_by_id(&self, id: usize) -> &ItemTraitReturns {
19 | | let temp: &ItemTraitReturns<'static> = self.map.get(&id).unwrap();
20 | | // Error:
21 | | // = note: expected type `&demo2::ItemTraitReturns<'_>`
22 | | // found type `&demo2::ItemTraitReturns<'static>`
23 | | temp
24 | | }
| |_____^
= note: ...does not necessarily outlive the static lifetime
Working example with my own SmallVec
When I implement my own (very naive) SmallVec<[T; 2]> (called NaiveSmallVec2<T>) the code also compiles... Very strange!
use std::borrow::Cow;
use std::collections::HashMap;
/// This is a very naive implementation of a SmallVec<[T; 2]>
pub struct NaiveSmallVec2<T> {
item1: Option<T>,
item2: Option<T>,
more: Vec<T>,
}
impl<T> NaiveSmallVec2<T> {
pub fn push(&mut self, item: T) {
if self.item1.is_none() {
self.item1 = Some(item);
} else if self.item2.is_none() {
self.item2 = Some(item);
} else {
self.more.push(item);
}
}
pub fn element_by_index(&self, index: usize) -> Option<&T> {
match index {
0 => self.item1.as_ref(),
1 => self.item2.as_ref(),
_ => self.more.get(index - 2),
}
}
}
pub trait MyTrait {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns;
}
/// IMPORTANT PART IS HERE: Uses NaiveSmallVec2
pub struct ItemTraitReturns<'a>(NaiveSmallVec2<Cow<'a, str>>);
/// only takes items with static lifetime
pub struct MyTraitStruct {
map: HashMap<usize, ItemTraitReturns<'static>>,
}
impl MyTrait for MyTraitStruct {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns {
let temp: &ItemTraitReturns<'static> = self.map.get(&id).unwrap();
// astonishingly this works!
temp
}
}
I expect the SmallVec version to compile like the Vec version does. I don't understand why in some cases (in the case of Vec) &ItemTraitReturns<'static> can be converted to &ItemTraitReturns<'_> and in some cases (SmallVec) it's not possible (I don't see the influence of Vec / SmallVec).
I don't want to change lifetimes of this trait:
pub trait MyTrait {
fn get_by_id(&self, id: usize) -> &ItemTraitReturns;
}
... since when using the trait I don't care about lifetimes (this should be an implementation detail) ... but still would like to use SmallVec.
This difference appears to be caused by a difference in variance between Vec and SmallVec. While Vec<T> is covariant in T, SmallVec appears to be invariant. As a result, SmallVec<[&'a T; 1]> can't be converted to SmallVec<[&'b T; 1]> even when lifetime 'a outlives 'b and the conversion should be safe. Here is a minimal example triggering the compiler error:
fn foo<'a, T>(x: SmallVec<[&'static T; 1]>) -> SmallVec<[&'a T; 1]> {
x // Compiler error here: lifetime mismatch
}
fn bar<'a, T>(x: Vec<&'static T>) -> Vec<&'a T> {
x // Compiles fine
}
This appears to be a shortcoming of SmallVec. Variance is automatically determined by the compiler, and it is sometimes cumbersome to convince the compiler that it is safe to assume that a type is covariant.
There is an open Github issue for this problem.
Unfortunately, I don't know a way of figuring out the variance of a type based on its public interface. Variance is not included in the documentation, and depends on the private implementation details of a type. You can read more on variance in the language reference or in the Nomicon.
I have a trait in which I want to provide a method. The method is to be implemented in terms of some helpers that have no business being inside the trait and are non-trivial enough that dynamic polymorphism makes more sense than making them generic. So I have code along the lines of
fn use_trait(x: &Trait) {
println!("object says {}", x.needed());
}
trait Trait {
fn needed(&self) -> &str;
fn provided(&self) {
use_trait(self);
}
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
}
fn main() {
Struct().provided();
}
Which, however, does not compile, with error:
error[E0277]: the trait bound `Self: std::marker::Sized` is not satisfied
--> <anon>:9:19
|
9 | use_trait(self);
| ^^^^ the trait `std::marker::Sized` is not implemented for `Self`
|
= help: consider adding a `where Self: std::marker::Sized` bound
= note: required for the cast to the object type `Trait`
I understand why—it is not guaranteed somebody won't implement the trait for an unsized type (converting from &T where T: Trait to &Trait requires T: Sized, but the declaration does not require that).
However, the advice will not do what I need. I can add
fn needed(&self) -> &str where Self: Sized
but then the needed() method won't be accessible on &Trait (because Trait : ?Sized), which renders the thing useless, because the type (the actual one that does something useful) is always handled as Arc<Trait>. And adding
trait Trait: Sized
is even worse, because that does not permit &Trait at all (Trait as a type is unsized, so Trait type does not implement trait Trait).
Of course I can simply make
fn use_trait<T: Trait>(x: &T)
but there is a lot behind it in the real code, so I don't want monomorphisation there especially since the trait is otherwise always handled as trait object.
Is there any way to tell Rust that all types that impl Trait must be sized and here is a definition of a method that should work for all of them?
You need an additional as_trait function on Trait and its implementations:
trait Trait {
fn needed(&self) -> &str;
fn provided(&self) {
use_trait(self.as_trait());
}
fn as_trait(&self) -> &Trait;
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
fn as_trait(&self) -> &Trait {
self as &Trait
}
}
You can try it on the playground. (trait objects)
Enhanced version of #JoshuaEntrekin's answer:
The helper as_trait function can be put in an auxiliary trait that gets blanket implementation for all Sized types trying to implement Trait. Then the implementer of Trait does not have to do anything special and the conversion works.
fn use_trait(x: &Trait) {
println!("object says {}", x.needed());
}
trait Trait : AsTrait {
fn needed(&self) -> &str;
fn provided(&self) where Self : AsTrait {
use_trait(self.as_trait());
}
}
trait AsTrait {
fn as_trait(&self) -> &Trait;
}
impl<T : Trait + Sized> AsTrait for T {
fn as_trait(&self) -> &Trait { self }
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
}
fn main() {
Struct().provided();
}
(on play).
It would also be possible to simply put provided in the auxiliary trait, but then it would have to dynamically dispatch to the other methods of Self unnecessarily.
Update: Actually, the point is that it should still be possible to override provided.
Now the above can be improved further by making it generic. There is std::makrer::Unsize, which is unstable at the time of this writing. We can't make
trait Trait : Unsize<Trait>
because Rust does not allow CRTP, but fortunately it is enough to put the constraint on the method. So
fn use_trait(x: &Trait) {
println!("object says {}", x.needed());
}
trait Trait {
fn needed(&self) -> &str;
fn provided(&self) where Self: AsObj<Trait> {
use_trait(self.as_obj());
}
}
trait AsObj<Tr: ?Sized> {
fn as_obj(&self) -> &Trait;
}
// For &'a Type for Sized Type
impl<Type: Trait> AsObj<Trait> for Type {
fn as_obj(&self) -> &Trait { self }
}
// For trait objects
impl AsObj<Trait> for Trait {
fn as_obj(&self) -> &Trait { self }
}
struct Struct();
impl Trait for Struct {
fn needed(&self) -> &str {
"Hello, world!"
}
fn provided(&self) {
println!("Aber dieses Objekt sagt Grüß Gott, Welt!"); // pardon my German, it is rusty.
}
}
fn main() {
let s: &Trait = &Struct();
s.provided();
}
(on play)
This finally makes it transparent for the implementors of other versions.
See also this users thread.