Make a generic trait that holds something that implements a separate trait? - rust

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

Related

Implement methods for trait without additional traits

Looking for "blanket" implementation of the method(s) for trait.
Let's say for a trait
pub trait A {
fn do_a(&self);
}
want to have boxed method that wraps with box, without introducing any additional traits:
fn boxed(self) -> Box<Self>;
I can have another trait to achieve that (playground)
pub trait A {
fn do_a(&self);
}
pub trait Boxed {
fn boxed(self) -> Box<Self>;
}
impl<T> Boxed for T
where
T: A,
{
fn boxed(self) -> Box<Self> {
Box::new(self)
}
}
However, new trait Boxed is required for that.
You can add boxed directly to A with a default implementation so that structs won't need to implement it themselves:
trait A {
fn do_a(&self);
fn boxed (self) -> Box<Self>
where Self: Sized
{
Box::new (self)
}
}
struct Foo{}
impl A for Foo {
fn do_a (&self) {
todo!();
}
// No need to redefine `boxed` here
}
fn main() {
let foo = Foo{};
let _object: Box<dyn A> = foo.boxed();
}
Playground

How can a Rust trait object return another trait object?

How can I attempt something like the following in Rust?
The builder class is a trait object which returns another trait object (type erasure) where the implementation that is selected is defined by the specific object of the builder trait that we are using.
trait Builder {
// I want this trait to return a trait object
fn commits(&self) -> dyn Commit;
fn finish(&self);
}
trait Commit {
}
struct FooBuilder {
}
struct FooCommit {
}
impl Builder for FooBuilder {
fn commits(&self) -> impl Commit {
FooCommit{ }
}
fn finish(&self) {
}
}
fn get_commits(b: &Builder) {
// trait object returns a trait
let c = b.commits();
}
fn main() {
let b = FooBuilder{};
get_commits(&b);
b.finish();
}
There is no problem returning trait objects from trait methods in Rust:
trait Foo {
fn bar(&self) -> Box<dyn Bar>;
}
One thing to notice is that you need to return Box<dyn Bar>, not dyn Bar, since size of dyn Bar is not known at compiled time, which renders it useless.
When you implement this trait, the signature has to match, so it should return Box<dyn Bar>, not impl Bar:
impl Foo for MyFoo {
fn bar(&self) -> Box<dyn Bar> {
Box::new(MyBar{})
}
}

Why sized trait is required for a builder function to generate Rc<T>?

This code works fine (playground):
use std::rc::Rc;
trait Foo {
fn foo(&self);
}
struct Bar<T> {
v: Rc<T>,
}
impl<T> Bar<T> where
T: Foo {
fn new(rhs: Rc<T>) -> Bar<T> {
Bar{v: rhs}
}
}
struct Zzz {
}
impl Zzz {
fn new() -> Zzz {
Zzz{}
}
}
impl Foo for Zzz {
fn foo(&self) {
println!("Zzz foo");
}
}
fn make_foo() -> Rc<Foo> {
Rc::new(Zzz{})
}
fn main() {
let a = Bar::new(Rc::new(Zzz::new()));
a.v.as_ref().foo()
}
but if I make a wrapper to generate Rc like below, the compiler complains about missing sized trait (playground)
fn make_foo() -> Rc<dyn Foo> {
Rc::new(Zzz::new())
}
fn main() {
let a = Bar::new(make_foo());
a.v.as_ref().foo()
}
in both cases, Bar::new received parameters with same type Rc, why the rust compiler reacts different?
By default, all type variables are assumed to be Sized. For example, in the definition of the Bar struct, there is an implicit Sized constraint, like this:
struct Bar<T: Sized> {
v: Rc<T>,
}
The object dyn Foo cannot be Sized since each possible implementation of Foo could have a different size, so there isn't one size that can be chosen. But you are trying to instantiate a Bar<dyn Foo>.
The fix is to opt out of the Sized trait for T:
struct Bar<T: ?Sized> {
v: Rc<T>,
}
And also in the context of the implementations:
impl<T: ?Sized> Bar<T>
where
T: Foo
?Sized is actually not a constraint, but relaxing the existing Sized constraint, so that it is not required.
A consequence of opting out of Sized is that none of Bar's methods from that impl block can use T, except by reference.

Is it possible to use `impl Trait` as a function's return type in a trait definition?

Is it at all possible to define functions inside of traits as having impl Trait return types? I want to create a trait that can be implemented by multiple structs so that the new() functions of all of them returns an object that they can all be used in the same way without having to write code specific to each one.
trait A {
fn new() -> impl A;
}
However, I get the following error:
error[E0562]: `impl Trait` not allowed outside of function and inherent method return types
--> src/lib.rs:2:17
|
2 | fn new() -> impl A;
| ^^^^^^
Is this a limitation of the current implementation of impl Trait or am I using it wrong?
As trentcl mentions, you cannot currently place impl Trait in the return position of a trait method.
From RFC 1522:
impl Trait may only be written within the return type of a freestanding or inherent-impl function, not in trait definitions or any non-return type position. They may also not appear in the return type of closure traits or function pointers, unless these are themselves part of a legal return type.
Eventually, we will want to allow the feature to be used within traits [...]
For now, you must use a boxed trait object:
trait A {
fn new() -> Box<dyn A>;
}
See also:
Is it possible to have a constructor function in a trait?
Why can a trait not construct itself?
How do I return an instance of a trait from a method?
Nightly only
If you wish to use unstable nightly features, you can use existential types (RFC 2071):
// 1.67.0-nightly (2022-11-13 e631891f7ad40eac3ef5)
#![feature(type_alias_impl_trait)]
#![feature(return_position_impl_trait_in_trait)]
trait FromTheFuture {
type Iter: Iterator<Item = u8>;
fn returns_associated_type(&self) -> Self::Iter;
// Needs `return_position_impl_trait_in_trait`
fn returns_impl_trait(&self) -> impl Iterator<Item = u16>;
}
impl FromTheFuture for u8 {
// Needs `type_alias_impl_trait`
type Iter = impl Iterator<Item = u8>;
fn returns_associated_type(&self) -> Self::Iter {
std::iter::repeat(*self).take(*self as usize)
}
fn returns_impl_trait(&self) -> impl Iterator<Item = u16> {
Some((*self).into()).into_iter()
}
}
fn main() {
for v in 7.returns_associated_type() {
println!("type_alias_impl_trait: {v}");
}
for v in 7.returns_impl_trait() {
println!("return_position_impl_trait_in_trait: {v}");
}
}
If you only need to return the specific type for which the trait is currently being implemented, you may be looking for Self.
trait A {
fn new() -> Self;
}
For example, this will compile:
trait A {
fn new() -> Self;
}
struct Person;
impl A for Person {
fn new() -> Person {
Person
}
}
Or, a fuller example, demonstrating using the trait:
trait A {
fn new<S: Into<String>>(name: S) -> Self;
fn get_name(&self) -> String;
}
struct Person {
name: String
}
impl A for Person {
fn new<S: Into<String>>(name: S) -> Person {
Person { name: name.into() }
}
fn get_name(&self) -> String {
self.name.clone()
}
}
struct Pet {
name: String
}
impl A for Pet {
fn new<S: Into<String>>(name: S) -> Pet {
Pet { name: name.into() }
}
fn get_name(&self) -> String {
self.name.clone()
}
}
fn main() {
let person = Person::new("Simon");
let pet = Pet::new("Buddy");
println!("{}'s pets name is {}", get_name(&person), get_name(&pet));
}
fn get_name<T: A>(a: &T) -> String {
a.get_name()
}
Playground
As a side note.. I have used String here in favor of &str references.. to reduce the need for explicit lifetimes and potentially a loss of focus on the question at hand. I believe it's generally the convention to return a &str reference when borrowing the content and that seems appropriate here.. however I didn't want to distract from the actual example too much.
You can get something similar even in the case where it's not returning Self by using an associated type and explicitly naming the return type:
trait B {}
struct C;
impl B for C {}
trait A {
type FReturn: B;
fn f() -> Self::FReturn;
}
struct Person;
impl A for Person {
type FReturn = C;
fn f() -> C {
C
}
}
Fairly new to Rust, so may need checking.
You could parametrise over the return type. This has limits, but they're less restrictive than simply returning Self.
trait A<T> where T: A<T> {
fn new() -> T;
}
// return a Self type
struct St1;
impl A<St1> for St1 {
fn new() -> St1 { St1 }
}
// return a different type
struct St2;
impl A<St1> for St2 {
fn new() -> St1 { St1 }
}
// won't compile as u32 doesn't implement A<u32>
struct St3;
impl A<u32> for St3 {
fn new() -> u32 { 0 }
}
The limit in this case is that you can only return a type T that implements A<T>. Here, St1 implements A<St1>, so it's OK for St2 to impl A<St2>. However, it wouldn't work with, for example,
impl A<St1> for St2 ...
impl A<St2> for St1 ...
For that you'd need to restrict the types further, with e.g.
trait A<T, U> where U: A<T, U>, T: A<U, T> {
fn new() -> T;
}
but I'm struggling to get my head round this last one.

Enforce Eq check on trait

Suppose the following objects:
pub struct MyStruct<T>{
items: Vec<T>
}
pub impl<T> MyStruct<T> {
pub fn new() -> MyStruct {
MyStruct{ items::new(); }
}
pub fn add(&mut self, item: T) where T : Eq {
self.items.push(item);
}
}
pub trait C {}
pub struct Another {
mystruct: MyStruct<Box<C>>
}
pub impl Another {
pub fn exec<C>(&mut self, c: C) where C: Eq + C {
self.mystruct.add(c);
}
}
On exec I'm enforcing that C also is Eq, but I'm receiving the following error:
error: the trait `core::cmp::Eq` is not implemented for the type `C`
I had to make
pub impl<T> MyStruct<T>
instead of
pub impl<T : Eq> MyStruct<T>
because since C is a trait I cant enforce Eq when using MyStruct::new, so I left the check for the type guard on the function. What's happening here?
Let’s look at the relevant definitions for Eq:
pub trait Eq: PartialEq<Self> {
…
}
pub trait PartialEq<Rhs: ?Sized = Self> {
fn eq(&self, other: &Rhs) -> bool;
…
}
Now consider MyStruct<Box<C>>: the type that it wants to implement Eq is Box<C>, a boxed trait object. To implement Eq, Box<C> must first implement PartialEq<Box<C>>, like this:
impl PartialEq for Box<C> {
fn eq(&self, other: &Box<C>) -> bool;
}
impl Eq for Box<C> { }
That is, you must be able to compare an arbitrary Box<C> with any other arbitrary Box<C>. The boxed trait objects that you are comparing could be of different concrete types, here. Thus you need to write this implementation manually. In such cases, you will typically want the trait to include some way of normalising the form of the object into a concrete, comparable type; for some types this is obvious; if AsRef<T> was to have a PartialEq implementation added, the implementation to add would be fairly obvious:
impl<T: PartialEq> PartialEq for AsRef<T> {
fn eq(&self, other: &AsRef<T>) -> bool {
self.as_ref() == other.as_ref()
}
}
(Note also that if C implements PartialEq, Box<C> does, so such implementations as we’re discussing should go on the unboxed trait object, not on the boxed trait object.)
Quite often, however, there is not an obvious and simple implementation. There are a few approaches you could take:
Convert the objects (potentially expensively, though ideally cheaply) into some base type, e.g. a String which can then be compared;
Give up;
Constrain C to 'static types and use some fancy Any magic to make it so that it uses the base type’s PartialEq implementation, returning false if the two trait objects are not of the same concrete type. This one is a fairly useful one, so I’ll give some code for it:
#![feature(core)]
use std::any::{Any, TypeId};
use std::mem;
fn main() { }
trait PartialEqFromC {
fn eq_c(&self, other: &C) -> bool;
}
impl<T: PartialEq + Any + C> PartialEqFromC for T {
fn eq_c(&self, other: &C) -> bool {
if other.get_type_id() == TypeId::of::<Self>() {
self == unsafe { *mem::transmute::<&&C, &&Self>(&other) }
} else {
false
}
}
}
trait C: Any + PartialEqFromC {
}
impl PartialEq for C {
fn eq(&self, other: &C) -> bool {
self.eq_c(other)
}
}
Note that this example depends on the unstable feature core for Any.get_type_id and is thus tied to nightly only; this can be worked around by duplicating that definition from the Any trait into a new supertrait of C and could also be simplified by mopafying the C trait.

Resources