Typestate struct graph relations - rust

I have an (acyclic) tree structure using some structs. I want to codify the relations at the type-level so I can get guarantees on whether a given struct is a descendant of another.
Would it be possible to implement a macro- or blanket implementation for DescendantOf<Ancestor>? Or is there a way with associated types, even at arbitrary depth?
Struct graph
#[Derive(ParentChildRelation)]
struct GrandFather {
father: Father
}
#[Derive(ParentChildRelation)]
struct Father {
child: Child
}
#[Derive(ParentChildRelation)]
struct Child {}
Relation traits
// automatically implemented using Derive
pub trait ParentOf<Child> {}
// automatically implemented using Derive
pub trait ChildOf<Parent> where Parent: ParentOf<Self> {}
// automatically implemented using impl constraint
pub trait Descendant2<Root, Parent>
where
Root: ParentOf<Parent>,
Parent: ChildOf<Root> + ParentOf<Self>,
Self: ChildOf<Parent>
{}
pub trait Descendant3<Root, GrandFather, Parent>
where
Root: ParentOf<GrandFather>,
GrandFather: ChildOf<Root> + ParentOf<Parent>,
Parent: ChildOf<GrandFather> + ParentOf<Self>,
Self: ChildOf<Parent>
{
// and so on...
...
Blanket implementations
// blanket implement all variants 2, 3, n...
impl<Ancestor, GrandFather, Parent, T> Descendant3<Ancestor, GrandFather, Parent>
for T
where
Ancestor: ParentOf<GrandFather>,
GrandFather: ChildOf<Ancestor> + ParentOf<Parent>,
Parent: ChildOf<GrandFather> + ParentOf<T>,
T: ChildOf<Parent>
{}
// this all works
...
My goal is being able to do something like:
fn test(descendant:Obj) where Obj: DescendantOf<GrandParent>
But I don't see a way of implementing a DescendantOf<Ancestor> trait based on the more extensive DescendantX<A, B, C, ...> traits, since:
// todo: this doesnt work... how do we now get DescendantOf<Ancestor> implemented for all *X variants? this results in 'the type parameter B, C is not constrained by the impl trait, self type, or predicates' error...
impl<T: Descendant3<Ancestor, B, C>, A, B, C> DescendantOf<Ancestor> for T {}

Related

Use a generic type as associated type, without phantom data

I have a trait defining an associated type:
pub trait View: Default {
type State;
fn draw(&mut self, state: &mut Self::State);
}
I have a type suitable for the State associated type, but it's generic:
pub struct MenuState<I> {
pub items: Vec<I>,
}
The following code can't compile (rust 1.66) because I isn't constrained:
#[derive(Default)]
pub struct MenuView {
// fields here
}
impl<I: ToString + Copy> View for MenuView {
type State = MenuState<I>;
fn draw(&mut self, _state: &mut Self::State) { }
}
(note: the ToString and Copy constraints are needed for the implementation of MenuView)
I know how to solve that by adding some phantom data to make MenuView generic on I (see playground) but it's ugly and seems too much complicated. I feel like the code pasted above just needs some syntactic fix.
Is there a simple solution, without changing the View trait and not involving a phantom ?
If a syntactic fix can't be defined for some fundamental reason, what is that reason ?
Without changing the View trait, PhantomData is the correct solution, your generic must appear somewhere either in the trait or in the struct. State is something that is "associate" to MenuView and so there is no other way than to include it.
However, your case could suggest View is not well defined to your usage, maybe a generic would be more appropriate than an associate type. Like:
pub trait View<State>: Default {
fn draw(&mut self, state: &mut State);
}
#[derive(Default)]
pub struct MenuState<I> {
pub items: Vec<I>,
}
#[derive(Default)]
pub struct MenuView {}
impl<I: ToString + Copy + Default> View<MenuState<I>> for MenuView {
fn draw(&mut self, _state: &mut MenuState<I>) {}
}
This more or less transforms your original trait, from, implement View for MenuView that have a State, to, implement View a State for MenuView.

Pattern matching for get the desired type on a impl statement

I have these two types:
pub struct A;
pub struct B;
The following trait:
pub trait MyTrait {}
and a free function:
fn do_something(mut element: impl MyTrait) { ... }
I want to, based on the real type behind the impl, do something different depending the type of the element that is coming in the current function call.
// pseudo-snippet
match element {
A => do something with A,
B => do something with B
}
Is possible in Rust, determine the type behind an impl statement? Or, at least, make some decision making workflow based (if-else branches, patter matching...) on that impl?
The way to do this is to add a method to MyTrait, which does the two different things you want, or which returns an enum that you can match.
pub trait MyTrait {
fn do_something(self);
}
There is no mechanism to directly branch on what a concrete type is in the way you mean. Technically you could achieve it with std::any::TypeId, but you shouldn't — because you're defeating static checking, making it possible for an error in your program (not handling the right set of types) to be delayed until run time. Instead, by putting the functionality into MyTrait, your program has the property that if some type implements MyTrait, there is necessarily a definition of what do_something should do with that trait.
There is no syntax for match on types. The only way to do any kind of "downcasting" is via the Any trait. You can use it with an impl MyTrait if it also constrained to be 'static:
use std::any::Any;
pub struct A;
pub struct B;
pub struct C;
pub trait MyTrait {}
impl MyTrait for A {}
impl MyTrait for B {}
impl MyTrait for C {}
fn do_something(mut element: impl MyTrait + 'static) {
let element = &mut element as &mut dyn Any;
if let Some(a) = element.downcast_mut::<A>() {
// do something with `a`
println!("is A");
} else if let Some(b) = element.downcast_mut::<B>() {
// do something with `b`
println!("is B");
} else {
// do something with `element`
println!("is unknown");
}
}
fn main() {
do_something(A);
do_something(B);
do_something(C);
}
is A
is B
is unknown
The two caveats with this code are 1) the 'static constraint, meaning you can't use this with traits or implementations with non-static generic lifetimes; and 2) you can't get an owned version of A or B (the type of a is &mut A in the above code).
This type of code pattern is very un-idiomatic. If you want to have trait instances exhibit different behavior based on their implementations, you should have an associated method for it:
pub trait MyTrait {
fn do_something(self)
}
Or if you want to implement behavior on a closed-set of types with shared behavior, you should instead use an enum:
enum MyEnum {
A(A),
B(B),
}
fn do_something(element: MyEnum) {
match element {
MyEnum::A(a) => { println!("is A"); },
MyEnum::B(b) => { println!("is B"); },
}
}
With either of these methods, you don't have the same caveats of trying to circumvent the polymorphic trait system.
See also (none address the impl Trait case, but they pretty much all say the same thing):
How to match trait implementors
How do you match a trait in rust?
How to match on data type in Rust?

What does it mean when we let a trait inherits 'static?

Rust supports trait inheritance, as follows:
pub trait A {}
pub trait B: A {}
B: A means that if some type T implements B, it also needs to implement all the methods in A.
But today I see the following code:
trait Display: 'static {
fn print(&self);
}
What does it mean? It doesn't seem to be trait inheritance.
Rust supports trait inheritance, as follows [...] B: A means that if some type T implements B, it also needs to implement all the methods in A.
Technically, that is not inheritance but requirement. It is a trait bound not entirely dissimilar to one you'd have in a function: it constraints the type on which B is implementable to only types on which A is already implemented.
With that change in wording, the second version is much easier to understand: it's a lifetime bound, meaning it constraints the type on which B is implementable to only types with 'static lifetime, meaning if you're trying to implement B on a type, that must either have no lifetime at all, or have a 'static lifetime (or the implementation must have a lifetime bound aka only work for some uses of the type).
You can see that if you try to implement the trait on a lifetime-generic structure:
struct A<'a>(&'a str);
trait Display: 'static {
fn print(&self);
}
impl <'a>Display for A<'a> {
fn print(&self) { todo!() }
}
will yield
error[E0478]: lifetime bound not satisfied
That is because 'a can be anything, so implementing Display for A<'a> means it is also implemented for non-'static instances, which is not valid.
By adding the relevant lifetime bound on the impl, and thus limiting the implementation to A<'static> instances:
struct A<'a>(&'a str);
trait Display: 'static {
fn print(&self);
}
impl <'a: 'static>Display for A<'a> {
fn print(&self) { todo!() }
}
the requirements of the trait are satisfied, and the impl is valid (nb: the 'a is not necessary here you can just impl ... for A<'static>, I'm showing it for regularity).
And if your struct has no lifetime it works by default, because no lifetime ~ 'static:
struct A(String);
trait Display: 'static {
fn print(&self);
}
impl Display for A {
fn print(&self) { todo!() }
}
Rust doesn't have inheritance.
What it has is a way to define constraints. For example a trait may be constrained to only be implemented by types which implement another trait.
In your case the constraint is a lifetime bound.
To implement your Display trait, an object may contain references but in this case their lifetime must respect this constraint.
Let's suppose you have this type:
struct S<'a> {
s: &'a str,
}
Then you can't implement the trait for any lifetime, but only 'static.
impl Display for S<'static> {
fn print(&self){}
}
fn main() {
let s1 = "test";
let a = S { s: s1 };
a.print(); // compiles
let s2 = "test".to_string();
let a = S { s: &s2 };
a.print(); // doesn't compile because s doesn't live long enough
}

Struct fields should be all of same trait, but not neceesarily same type

I am trying to implement the following trait and struct:
pub trait Funct {
fn arity(&self) -> u32;
}
#[derive(Debug, Hash, Eq, PartialEq, Clone)]
pub struct FunctionLiteral<T: Funct> {
pub function: T,
pub args: Vec< FunctionLiteral<T> >
}
pub enum Foo {
Foo
}
impl Funct for Foo {
fn arity(&self) -> u32 {0}
}
pub enum Bar {
Bar
}
impl Funct for Bar {
fn arity(&self) -> u32 {0}
}
fn main() {
let baz = FunctionLiteral{
function: Foo::Foo,
args: vec![FunctionLiteral{
function: Bar::Bar,
args: vec![]
}]
};
}
I can set it up the way I have for the generic type T to be of trait Funct, but I don't necessarily want T to be of the same type.
Here, compiling the code gives the following error:
error[E0308]: mismatched types
--> foo.rs:31:23
|
31 | function: Bar::Bar,
| ^^^^^^^^ expected enum `Foo`, found enum `Bar`
error: aborting due to previous error
Is it possible to set up FunctionLiteral such that I can have different types for function and the items of args, while forcing both of them to be of type Funct?
The problem
When you do:
Structure<T: Trait>{
inner: T,
many: Vec<T>
}
You are telling the compiler to create a specialized instance for each different T. So if you have Foo and Bar both implementing Trait, then the compiler will generate two different representations, with two different sizes:
struct Foo(u8);
impl Trait for Foo{
// impl goes here
}
struct Bar(u64);
impl Trait for Bar{
// impl goes here
}
Then the compiler will generate something like:
Structure<Foo>{
inner: Foo,
many: Vec<Foo>
}
// and
Structure<Bar>{
inner: Bar,
many: Vec<Bar>
}
Obviously you cannot put Foo instances into Bar as they are different types and have different sizes.
The solution
You need to Box<> your Funct types in order to make them the same size (i.e. pointer sized). By putting them behind a (smart) pointer you are essentially erasing their types:
let a: Box<dyn Trait> = Box::new(Foo(0));
let b: Box<dyn Trait> = Box::new(Bar(0));
Now both a and b have the same size (the size of a pointer) and have the same type - Box<dyn Trait>. So now you can do:
struct Structure{ // no longer generic!
inner: Box<dyn Trait>, // can hold any `Box<dyn Trait>`
many: Vec<Box<dyn Trait>> // can hold any `Box<dyn Trait>`
}
The downside of this approach is that it requires heap allocation and that it looses the exact type of a and b. Youno longer know if a is Foo or Bar or something else.
Instead of Box you can use any other smart pointer, such as Rc or Arc if you need its functionality.
Another option
Another option is to make Foo and Bar the same size and type. This can be done by wrapping them in an enum:
enum Holder{
Foo(Foo),
Bar(Bar), // any other types go here in their own variants
}
Then your structure will look like:
struct Structure{ // no longer generic!
inner: Holder, // can hold any Holder variant`
many: Vec<Holder> // can hold any Holder variant`
}
The downside is that you have to either implement a delegation like:
impl Trait for Holder{
fn some_method(&self){
match self{
Holder::Foo(foo) => foo.some_method(),
Holder::Bar(bar) => bar.some_method(),
}
}
}
Or match everywhere you want to use the object. Also now your Holder enum will be the size of max(sizeof(Foo), sizeof(Bar))
On the plus side:
you still know the actual type - it's not erased
no heap allocation

Generic APIs with traits

I'm attempting to generalize/decouple an API, so that alternative structs that satisfy a Trait can be used. Struggling on Syntax regarding nested traits. This is a stripped down example of what I'm attempting to do. Note that in the real one, there are multiple sub-traits, which potentially makes a follow-on question of "how to reduce verbosity, if I'm on the right track".
pub mod general {
/// A trait to make the API generic
pub trait MyTrait<A: PartialEq> {
// type A: Partial
fn val(self) -> A;
}
/// Part of the generic API
pub struct Data<A: PartialEq, T: MyTrait<A>> {
mys: T
}
/// Another part of the generic API
fn doit<A: PartialEq>(s: impl MyTrait<A>) -> impl MyTrait {
s
}
}
pub mod specific {
/// Api-specific substruct
#[derive(PartialEq)]
pub struct Aval {
val: u32
}
/// My top-level custom struct
pub struct MyS {
val: Aval
}
}
impl<A: PartialEq> crate::general::MyTrait<A> for crate::specific::MyS {
fn val(self) -> crate::specific::Aval {
self.val
}
}
/// Example of how we'd use this
fn main() {
let mys = crate::specific::MyS{ val: crate::specific::Aval{ val: 0 } };
let S = crate::general::Data{mys};
crate::general::doit(mys); // Eg pretend we cloned or didn't create S.
}
Playground
In this specific example, we have a chicken+egg: Error on the Data struct of error[E0392]: parameter `A` is never used. Where if we remove A: error[E0412]: cannot find type `A` in this scope
I suspect this is a syntax problem related to associated types.
Just remove the trait bound from your struct.
struct Data<T> {
mys: T
}
You can still add methods and trait implementations for Data<T> requiring more specific bounds on T, but you don’t need them to define the layout of Data<T>, so you shouldn’t specify them there (and in this case, you can’t, unless you add a PhantomData member referring to A).
Your code has a number of other errors, but I trust you’ll figure them out. Feel free to comment if you get stuck again.

Resources