Implementing Nested Traits - rust

I have some traits which (after removing functions and some parameter bloat) look like:
trait Foo { }
trait Boo { }
trait Bar<T: Foo> { }
trait Baz { }
If U implements Bar<T> for some T implementing Foo and U implements Boo, then one is able to derive an implementation of Baz for U. However, I wasn't able to write valid Rust code doing this.
A few tries were:
impl<T: Foo, U: Bar<T> + Boo> Baz for U { }
which gives
error: the type parameter T is not constrained by the impl trait, self type, or predicates [E0207]
whereas
impl<U: Bar<T> + Boo> Baz for U { }
yields
error: type name T is undefined or not in scope [E0412]
Could one/how could one do this in (stable) Rust (hopefully without any dynamic dispatch)?
Edit: Some people hinted at some similar questions for which there were essentially two approaches (and I find both of them unsuitable for my situation):
Using associated types. I don't want to do this because I want to keep track of T, e.g. I want to write some functions which have a signature like fn bla<T: Foo, U: Bar<T>, V: Bar<T>>() where I want to know that U and V implement Bar<T> for the same T. (Or is there way of doing this with associated types?)
Using some kind of wrapping by putting U and T in a struct. I don't want to use this either because I have several levels of such "trait dependencies", so wrapping things in each level would bloat the code a lot.
So the updated question would be: Is there a solution to this problem without using associated types or wrappers?

You can do it making T an associated type:
trait Foo { }
trait Boo { }
trait Bar {
type T: Foo;
}
trait Baz { }
impl<U: Bar + Boo> Baz for U
// this where clause is not necessary (this bound is already true)
// where U::T: Foo
{ }
I don't want to do this because I want to keep track of T, e.g. I want to write some functions which have a signature like fn bla<T: Foo, U: Bar<T>, V: Bar<T>>() where I want to know that U and V implement Bar<T> for the same T. (Or is there way of doing this with associated types?)
Yes, you can do it with associated types:
fn bla<U: Bar, V: Bar<T = U::T>>() { }

Related

How to assign an impl trait in a struct?

Consider some struct (HiddenInaccessibleStruct) that is not accessible, but implements an API trait. The only way to obtain an object of this hidden type is by calling a function, that returns an opaque implementation of this type. Another struct owns some type, that makes use of this API trait. Right now, it seems not possible to assign this field in fn new(). The code below can also be found in rust playgrounds.
// -- public api
trait Bound {
fn call(&self) -> Self;
}
// this is not visible
#[derive(Default)]
struct HiddenInaccessibleStruct;
impl Bound for HiddenInaccessibleStruct {
fn call(&self) -> Self { }
}
// -- public api
pub fn load() -> impl Bound {
HiddenInaccessibleStruct::default()
}
struct Abc<T> where T : Bound {
field : T
}
impl<T> Abc<T> where T : Bound {
pub fn new() -> Self {
let field = load();
Abc {
field // this won't work, since `field` has an opaque type.
}
}
}
Update
The API trait Bound declares a function, that returns Self, hence it is not Sized.
There are two concepts in mid-air collision here: Universal types and existential types. An Abc<T> is a universal type and we, including Abc, can refer to whatever T actually is as T (simple as that). impl Trait-types are Rust's closest approach to existential types, where we only promise that such a type exists, but we can't refer to it (there is no T which holds the solution). This also means your constructor can't actually create a Abc<T>, because it can't decide what T is. Also see this article.
One solution is to kick the problem upstairs: Change the constructor to take a T from the outside, and pass the value into it:
impl<T> Abc<T>
where
T: Bound,
{
pub fn new(field: T) -> Self {
Abc { field }
}
}
fn main() {
let field = load();
let abc = Abc::new(field);
}
See this playground.
This works, but it only shifts the problem: The type of abc in main() is Abc<impl Bound>, which is (currently) impossible to write down. If you change the line to let abc: () = ..., the compiler will complain that you are trying to assign Abc<impl Bound> to (). If you try to comply with the advice and change the line to let abc: Abc<impl Bound> = ..., the compiler will complain that this type is invalid. So you have to leave the type of abc being implied. This brings some useability issues with Abc<impl Bound>, because you can't easily put values of that type into other structs etc.; basically, the existential type "infects" the outer type containing it.
impl Trait-types are mostly useful for immediate consumption, e.g. impl Iterator<Item=...>. In your case, with the aim apparently being to hide the type, you may get away with sealing Bound. In a more general case, it may be better to use dynamic dispatch (Box<dyn Bound>).

Rust subtyping/"runtime polymorphism"/using traits in Boxes

Consider the following dumbed-down example:
pub trait ThemePark<A, V>
where
A: Attraction,
V: Visitor,
{
fn create(square_size: u32, name: &str) -> Self;
fn get_attractions(&self) -> Vec<A>;
fn get_visitors(&self) -> Vec<V>;
}
pub trait Attraction {}
pub trait Visitor {}
A concrete ThemePark could have an arbitrary set of attractions, as well as visitors. They are implemented with structs:
struct ElderlyWhiteMale;
impl Visitor for ElderlyWhiteMale {}
A ThemePark is wrapped in some company's assets, like so:
pub struct Asset<'a> {
name: &str,
theme_park: Box<ThemePark<> + 'a> // <-- This won't compile, as ThemePark needs 2 type arguments
}
This begins my pain. I put ThemePark in a Box because I don't know the size of it at compile time. It could be wrapped around any kind of Attraction and Visitor.
ThemePark needs 2 type arguments, but I can't know them at compile-time. Somewhere in my code I read this from an external file and build a ThemePark accordingly.
The idea is, that at runtime I can create a ThemePark from an external source and then invoke the functions defined in the trait on it.
impl Asset {
fn init_and_query() -> () {
let theme_park: Box<ThemePark> = match external_file.get_theme_park_type {
ThemeParkType::FUN => unimplemented! {"There is no fun, yet!"},
ThemeParkType::SERIOUS => {
println!("Creating serious themepark");
SeriousThemePark::create(size /*...*/)
}
};
let attractions = theme_park.get_attractions();
// ... Do something with the attractions
}
}
pub enum ThemeParkType {
FUN,
SERIOUS,
}
I understand that I can't put the ThemePark as-is on the stack... it's size is unknown at compile time, so the compiler can't know what to allocate.
That's why I either use a reference & or wrap it in a Box like I do here.
I understand there is type erasure, meaning that I would get back only a ThemePark and not a SeriousThemePark, but that would suffice for the moment.
Am I using traits all wrong here? How would you go and fix that. Coming from Java/Scala/C++ I seem to be stuck too deep in existing thinking.
Rust polymorphism is very similar to C++ polymorphism in that regard; it features:
compile-time polymorphism, to parameterize an item with types known at compile-time,
run-time polymorphism, when the concrete types are not known at compile-time.
Rust uses trait to define an interface which is then used to both constrain compile-time type parameters and serve as base-class/interface for run-time polymorphism, which is perhaps where your confusion comes from, however both kinds of polymorphism are inherently different.
pub struct Asset<'a> {
name: &str,
theme_park: Box<ThemePark<> + 'a> // <-- This won't compile, as ThemePark needs 2 type arguments
}
Then you should NOT be using compile-time polymorphism, and instead define ThemePark as:
pub trait ThemePark {
fn create(square_size: u32, name: &str) -> Self;
fn get_attractions(&self) -> Vec<Box<Attraction>>;
fn get_visitors(&self) -> Vec<Box<Visitor>>;
}
By instantiating a ThemePark<A, V> you create a theme park which can only ever contain one type of attractions (it's all Haunted Houses here, no Flume Ride sorry!) and one type of visitors (only Elderly Guys in, no Elderly Ladies or Kids).

Use Trait as Vec Type

I'm new to Rust and have seen some examples of people using Box to allow pushing many types that implement a certain Trait onto a Vec. When using a Trait with Generics, I have run into an issue.
error[E0038]: the trait `collision::collision_detection::Collidable` cannot be made into an object
--> src/collision/collision_detection.rs:19:5
|
19 | collidables: Vec<Box<Collidable<P, M>>>,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `collision::collision_detection::Collidable` cannot be made into an object
|
= note: method `get_ncollide_shape` has generic type parameters
error: aborting due to previous error
error: Could not compile `game_proto`.
To learn more, run the command again with --verbose.
Here is my code
extern crate ncollide;
extern crate nalgebra as na;
use self::ncollide::shape::Shape;
use self::ncollide::math::Point;
use self::ncollide::math::Isometry;
use self::na::Isometry2;
pub trait Collidable<P: Point, M> {
fn get_ncollide_shape<T: Shape<P, M>>(&self) -> Box<T>;
fn get_isometry(&self) -> Isometry2<f64>;
}
pub struct CollisionRegistry<P, M>
where
P: Point,
M: Isometry<P>,
{
collidables: Vec<Box<Collidable<P, M>>>,
}
impl<P: Point, M: Isometry<P>> CollisionRegistry<P, M> {
pub fn new() -> Self {
let objs: Vec<Box<Collidable<P, M>>> = Vec::new();
CollisionRegistry { collidables: objs }
}
pub fn register<D>(&mut self, obj: Box<D>)
where
D: Collidable<P, M>,
{
self.collidables.push(obj);
}
}
I'm trying to use collidables as a list of heterogenous game objects that will give me ncollide compatible Shapes back to feed into the collision detection engine.
EDIT:
To clear up some confusion. I'm not trying to construct and return an instance of a Trait. I'm just trying to create a Vec that will allow any instance of the Collidable trait to be pushed onto it.
Rust is a compiled language, so when it compiles your code, it needs to know all of the information it might need to generate machine code.
When you say
trait MyTrait {
fn do_thing() -> Box<u32>;
}
struct Foo {
field: Box<MyTrait>
}
you are telling Rust that Foo will contain a box containing anything implementing MyTrait. By boxing the type, the compiler will erase any additional data about the data type that isn't covered by the trait. These trait objects are implemented as a set of data fields and a table of functions (called a vtable) that contains the functions exposed by the trait, so they can be called.
When you change
fn do_thing() -> Box<u32>;
to
fn do_thing<T>() -> Box<T>;
it may look similar, but the behavior is much different. Let's take a normal function example
fn do_thing<T>(val: T) { }
fn main() {
do_thing(true);
do_thing(45 as u32);
}
the compiler performs what is a called monomorphization, which means your code in the compiler becomes essentially
fn do_thing_bool(val: bool) { }
fn do_thing_num(val: u32) { }
fn main() {
do_thing_bool(true);
do_thing_num(45 as u32);
}
The key thing to realize is that you are asking it to do the same thing for your trait. The problem is that the compiler can't do it. The example above relies on knowing ahead of time that do_thing is called with a number in one case and a boolean in another, and it can know with 100% certainty that those are the only two ways the function is used.
With your code
trait MyTrait {
fn do_thing<T>() -> Box<T>;
}
the compiler does not know what types do_thing will be called with, so it has no way to generate functions you'd need to call. To do that, wherever you convert the struct implementing Collidable into a boxed object it would have to know every possible return type get_ncollide_shape could have, and that is not supported.
Other links for this:
Understanding Traits and Object Safety
https://www.reddit.com/r/rust/comments/3an132/how_to_wrap_a_trait_object_that_has_generic/

How do I specify a generic type where one of the types is not needed?

I'm trying to use a generic datatype where one of the types is not needed (edge weights in a graph). I've been thinking to use the never type for this, which would look something like this:
#![feature(never_type)]
struct Foo<T> {
bar: T
}
impl<T> Foo<T> {
fn foo(&mut self, bar: T) {
self.bar = bar;
}
}
fn main() {
let mut foo: Foo<!> = Foo { bar: "nada" };
foo.foo("nada");
}
This obviously results in a type-mismatch for the "nada" placeholders, but just typing nothing will cause other errors. Is ! the right type to use here, and if so, what's the correct syntax?
I've gotten it to work using () instead of !, but I'm a bit unsure as to whether that's the proper choice of type. I believe in terms of efficiency it should make no difference, as () has no memory footprint?
() is the right choice. It is a type with a single value (also named ()), so it has a value, but contains no information.
! doesn't have any value, so if you put it in a struct the struct type doesn't have a value either and is basically unusable.

Lifetimes in traits

I'm currently diving into rust and writing a little math library.
The problem I like to solve is quite simple, I want to model fields and rings, but I can't get the lifetimes right.
here's the code:
ring.rs:
pub trait Ring {
fn characteristic() -> int;
fn is_unit(&self) -> bool;
fn is_field() -> bool;
}
field.rs:
use ring::Ring;
pub trait Field : Ring {
fn some_field_method() -> bool {
true
}
}
impl Ring for Field {
fn is_field() -> bool {
true
}
}
when compiling I get the following error:
/src/field.rs:9:15: 9:20 error: explicit lifetime bound required
/src/field.rs:9 impl Ring for Field {
^~~~~
I read the rust documentation, with the lifetime section and the rust-by-example section about it. The motivation behind lifetimes is obvious to me and I understand all the given examples. But here, I'm totally lost.
Btw: this is the minified version, I tried giving the Field a named lifetime, also the impl and Ring and various combinations of said.
May anyone explain what is happening here, or, if this is too specific, how to work with lifetimes and traits.
Thanks
You are trying to do some king of inheritance-like pattern with trait, which is not really how they work.
You can think of traits as somehow similar to interfaces provided by some languages : they are only a guaranty that your struct will provide some methods.
The syntax trait Foo : Bar does not mean that somehow the trait Foo is a superset of trait Bar, and that implementing Foo for a struct will implement Bar as well. It only states that trait Foo can only be implemented for structs already implementing trait Bar. You still need to implement both traits yourself.
Following your example, your approach would be something like this :
First, define the trait Ring:
pub trait Ring {
fn characteristic(&self) -> int;
fn is_unit(&self) -> bool;
fn is_field(&self) -> bool {
// Default value : not every Ring is a Field
false
}
}
Then, the trait Field, which requires the trait Ring
pub trait Field : Ring {
fn some_field_method(&self) -> bool {
// This is a default value as well
true
}
}
Then, your struct which will be both a Ring and a Field
struct MyField;
// First, implement Ring for MyField
impl Ring for MyField {
fn characteristic(&self) -> int {
2i
}
fn is_unit(&self) -> bool {
false
}
fn is_field(&self) -> bool {
// We override the default value : MyField is a Field
true
}
}
// Then we can implement Field for MyField
impl Field for MyField {
// Nothing here if we keep default implementation of some_field_method
}
Now, some explanations about this strange error with lifetimes.
When you wrote impl Ring for Field {...}, you where actually trying to implement the trait Ring for trait object Field.
A trait object is what you get when you use a reference to a struct like a reference to one of its traits, and they are quite peculiar to use, and require some playing with lifetimes.
However, in most situation you won't need to play with them, and classic generics will suffice.
You can have a look to my answer here, where I explain it with more details.

Resources