Higher ranked trait bounds and function parameters - rust

I'm trying to understand the implementation of Bevy's IntoForEachSystem trait and the way it interacts with the underlying Hecs Query and Fetch traits. Hecs has query types (the thing you request in a call to query::<T>) and item types (the thing returned by the query). The idea is that IntoForEachSystem is implemented for closures whose query type matches the query's item type, and fn f(&i32) works because an &i32 query returns an &i32 item.
I think I extracted the relevant parts of the design in this snippet, but I can't make it type check:
// Hecs Query trait
trait Query {
type Fetch: for<'a> Fetch<'a>;
}
// Hecs Query trait implementation for read-only references
impl<'a, T> Query for &'a T
where
T: 'static,
{
type Fetch = FetchRead<T>;
}
// Hecs Fetch trait
trait Fetch<'a>: Sized {
type Item;
}
// Hecs Fetch trait implementation for read-only references
struct FetchRead<T>(std::marker::PhantomData<T>);
impl<'a, T> Fetch<'a> for FetchRead<T>
where
T: 'static,
{
type Item = &'a T;
}
// Bevy IntoForEachSystem trait, simplified
trait IntoForEachSystem<R> {
fn system(self);
}
// Bevy IntoForEachSystem trait implementation for functions of one argument
impl<F, R> IntoForEachSystem<R> for F
where
F: Fn(R),
F: Fn(<<R as Query>::Fetch as Fetch>::Item),
R: Query,
{
fn system(self) {
println!("hello");
}
}
fn hmm(_x: &i32) {
todo!()
}
fn main() {
IntoForEachSystem::system(hmm)
}
Errors:
error[E0631]: type mismatch in function arguments
|
31 | fn system(self);
| ---------------- required by `IntoForEachSystem::system`
...
46 | fn hmm(_x: &i32) {
| ---------------- found signature of `for<'r> fn(&'r i32) -> _`
...
51 | IntoForEachSystem::system(hmm)
| ^^^ expected signature of `for<'r> fn(<FetchRead<i32> as Fetch<'r>>::Item) -> _`
|
= note: required because of the requirements on the impl of `IntoForEachSystem<&i32>` for `for<'r> fn(&'r i32) {hmm}`
I think the compiler is seeing the inferred lifetime 'r in fn hmm<'r>(&'r i32) as being different from the forall lifetime 'a in type Fetch: for<'a> Fetch<'a>. I don't see the trick that Bevy is using to achieve the same thing.

You're actually super close!
fn main() {
hmm.system();
}
This is ... very frustrating because IntoForEachSystem::system(hmm) should be equivalent to hmm.system() as far as I'm concerned. Maybe it's a bug in the Rust compiler?

Related

expected trait object `dyn Responsability`, found type parameter `T`

I am trying to implement a responsability chain in Rust:
link to playground
use std::error::Error;
struct Query {
query: String,
}
struct Response {
response: u64,
}
trait Responsability {
fn take(&self, iterator: std::slice::Iter<Box<dyn Responsability>>, query: Query) -> Result<Response, Box<dyn Error>>;
}
struct ResponsabilityChain<T: Responsability> {
responsabilities: Vec<Box<T>>,
}
impl<T: Responsability> ResponsabilityChain<T>
where
T: Responsability,
{
pub fn new(responsabilities: Vec<T>) -> Self {
let responsabilities = responsabilities.into_iter()
.map(|elt| Box::new(elt))
.collect();
Self { responsabilities }
}
pub fn launch(&self, query: Query) -> Result<Response, Box<dyn Error>> {
let iterator = self.responsabilities.iter();
let responsability = iterator.next().unwrap();
responsability.take(iterator, query)
}
}
fn main() {
println!("Hello, world!");
}
The infamous message is:
Compiling playground v0.0.1 (/playground) error[E0308]: mismatched
types --> src/main.rs:35:29 | 19 | impl<T: Responsability>
ResponsabilityChain | - this type parameter ... 35 |
responsability.take(iterator, query) |
^^^^^^^^ expected trait object dyn Responsability, found type
parameter T | = note: expected struct std::slice::Iter<'_, Box<(dyn Responsability + 'static)>>
found struct std::slice::Iter<'_, Box<T>> = help: type parameters must be constrained to match other types = note:
for more information, visit
https://doc.rust-lang.org/book/ch10-02-traits.html#traits-as-parameters
For more information about this error, try rustc --explain E0308.
error: could not compile playground due to previous error
I do not understand why the compiler complains expecting Box<dyn Responsability> while having Box<T> since I specify T: Responsability. What do I do wrong?
dyn I and <T> where T: I are different types in Rust, so the compiler complains since there's no implicit conversion.
T is a concrete type determined at compile time. dyn I it is a "trait object", it is dynamic, and concrete type is unknown, but sort of carried within.
A good video on the topic.
Conversion from <T> where T: I to dyn I is not free, it has a runtime cost, so has to be explicit with the Rust's philosophy.
The code could be fixed by using Vec<Box<dyn Responsability>> in all places. It will also allow you passing arbitrary types to new(), which is probably what you want, because Vec<T> has to contain objects of the same type (remember that this type is determined at compile time).

My T generic does implement the conditions on where, but I can't use it

struct Response {}
struct PlayResponse(Response);
struct DescribeResponse(Response);
impl From<Response> for PlayResponse {
fn from(response: Response) -> Self {
PlayResponse(response)
}
}
enum RtspState {
Init,
Playing,
}
struct RtspMachine {
state: RtspState
}
pub trait OnEvent<T> {
fn on_event(&mut self, event: &T) -> std::result::Result<(), ()>;
}
impl OnEvent<PlayResponse> for RtspMachine {
fn on_event(&mut self, event: &PlayResponse) -> std::result::Result<(), ()> {
self.state = RtspState::Playing;
Ok(())
}
}
fn do_something<T: OnEvent<T>>() where RtspMachine: OnEvent<T>, T: From<Response>{
let mut rtsp_machine = RtspMachine{state: RtspState::Init};
rtsp_machine.on_event(&T::from(Response{}));
rtsp_machine.on_event(&PlayResponse::from(Response{}));
}
On the do_something above, we require where RtspMachine: OnEvent<T>, T: From<Response>.
Note that RtspMachine: OnEvent<PlayResponse> and PlayResponse: From<Response>. I should be able to do rtsp_machine.on_event(&PlayResponse::from(Response{}));, but it only works for the version with T.:
Compiling playground v0.0.1 (/playground)
error[E0308]: mismatched types
--> src/lib.rs:36:27
|
33 | fn do_something<T: OnEvent<T>>() where RtspMachine: OnEvent<T>, T: From<Response>{
| - this type parameter
...
36 | rtsp_machine.on_event(&PlayResponse::from(Response{}));
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter `T`, found struct `PlayResponse`
|
= note: expected reference `&T`
found reference `&PlayResponse`
Rust playground
I know that
fn do_something<T>() where
RtspMachine: OnEvent<T> + OnEvent<PlayResponse>,
T: From<Response>
would work but I have lots of T that I wanted to use the specific type instead of generic T, so I can't just put them all on the where like that.
This is a known problem with the compiler's method resolution in the presence of trait bounds (#24066, #38071). Changing your method call
rtsp_machine.on_event(&PlayResponse::from(Response{}));
to the explicit function call form
OnEvent::<PlayResponse>::on_event(
&mut rtsp_machine, &PlayResponse::from(Response{}));
will allow the code to compile. Apparently, in the version that doesn't work, method resolution is looking only at the OnEvent<T> trait that's mentioned in where, even though OnEvent<PlayResponse> also exists.
I don't know if there's a more elegant solution, but perhaps the above will be adequate for your problem — at least it means the extra syntax is local to the call site.

implementing traits for dyn Fns

Today I was playing around with function traits. Though the example I show below might not practically be very useful, I do wonder why it doesn't compile.
pub fn do_something(o: &(dyn Other + 'static)) {
}
trait Other {
fn do_something_other(&self);
}
impl<A> Other for dyn Fn(A) {
fn do_something_other(&self) {
do_something(self);
}
}
Here I implement a trait for a function type. This function type is generic over it's parameter. This means that if you were to do it like this:
pub fn do_something(o: &(dyn Other + 'static)) {
}
trait Other {
fn do_something_other(&self);
}
impl<F, A> Other for F where F: (Fn(A)) + 'static {
fn do_something_other(&self) {
do_something(self);
}
}
you get an error stating a type parameter is unconstrained.
I get this and don't believe it's possible to do it with generics. But the dynamic approach, why doesn't it work? It gives the following error:
I don't understand this error. It states I pass a Fn(A) -> (), which doesn't implement Other. However, this error occurs literally in the implementation of Other. How can it not be implemented here?
My first thought was because each closure is its own type. If it has to do with this, I find the error very weird.
The first construction fails because you cannot convert a &dyn A into a &dyn B, even when implementing B for dyn A.
trait A {}
trait B {
fn do_thing(&self);
}
impl B for dyn A {
fn do_thing(&self) {
let b: &dyn B = self;
}
}
error[E0308]: mismatched types
--> src/lib.rs:9:25
|
9 | let b: &dyn B = self;
| ------ ^^^^ expected trait `B`, found trait `A`
| |
| expected due to this
|
= note: expected reference `&dyn B`
found reference `&(dyn A + 'static)`
Well, you can convert traits but only with help from the source trait. But since in this case the source is Fn, that's not a route.
The second construction fails because Rust won't let you implement traits that can conflict. Trying to implement B for a type that implements A<_> will automatically be rejected because types can have multiple implementations of A<_>.
trait A<T> {}
trait B {
fn do_thing(&self);
}
impl<T, U> B for T where T: A<U> {
fn do_thing(&self) {}
}
error[E0207]: the type parameter `U` is not constrained by the impl trait, self type, or predicates
--> src/lib.rs:7:9
|
7 | impl<T, U> B for T where T: A<U> {
| ^ unconstrained type parameter
Regarding Fns in particular, its somewhat hard to tell since usually function objects only implement a single Fn trait. However, the keyword is usually since you can enable a feature on nightly to do just that. And the trait system usually doesn't play favorites.
So what can you do? Well the first method is still functional, just you have to keep the implementation within the trait. You can use the second method if you use a concrete types for the function arguments.
You can conceivably implement Other for &dyn Fn(_) (implementing it on the reference and not the object itself). But that's not particularly convenient with how Fn objects are usually used.
pub fn do_something(o: &dyn Other) {}
trait Other {
fn do_something_other(&self);
}
impl<A> Other for &dyn Fn(A) {
fn do_something_other(&self) {
do_something(self);
}
}
fn main() {
// THIS WORKS
let closure: &dyn Fn(_) = &|x: i32| println!("x: {}", x);
closure.do_something_other();
// THIS DOESN'T WORK
// let closure = |x: i32| println!("x: {}", x);
// closure.do_something_other();
}
Another option would be to make the Other trait generic in order to constrain A, but that of course depends on how its designed to be used.

How to use non-'static trait objects with associated types?

I have this type:
struct Wrap<T>(Vec<T>);
I want to implement std::ops::Index and return references to trait objects. This was my first attempt (Playground):
use std::ops::Index;
use std::fmt::Display;
impl<T> Index<usize> for Wrap<T>
where
T: Display
{
type Output = Display;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
This doesn't work and leads to this error:
error[E0310]: the parameter type `T` may not live long enough
--> src/main.rs:13:9
|
7 | impl<T> Index<usize> for Wrap<T>
| - help: consider adding an explicit lifetime bound `T: 'static`...
...
13 | &self.0[index]
| ^^^^^^^^^^^^^^
|
note: ...so that the type `T` will meet its required lifetime bounds
--> src/main.rs:13:9
|
13 | &self.0[index]
| ^^^^^^^^^^^^^^
I think I know why this happens: type Output = Display is equivalent to type Output = Display + 'static as every trait object carries a lifetime bound which defaults to 'static.
So now I can just add the 'static bound to my parameter T, but this is over-constrained I think. I can easily implement such a method when not using an associated type:
impl<T> Wrap<T>
where
T: Display,
{
fn my_index(&self, index: usize) -> &Display {
&self.0[index]
}
}
No 'static bound needed, because now the signature desugars to:
fn my_index<'a>(&'a self, index: usize) -> &'a Display + 'a
Which makes sense: the trait object has to live for at least 'a. (Playground with all the code).
But can I make this work using associated types (like with the Index trait)? I have the feeling that this might work with generic associated types, but (a) I'm not sure and (b) they are not implemented yet.
One attempt is to attach a lifetime to the impl:
// Note: won't work.
impl<'a, T> Index<usize> for Wrap<T>
where
T: Display + 'a,
{
type Output = Display + 'a;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
However, the compiler will not accept it because 'a is not used.
error[E0207]: the lifetime parameter `'a` is not constrained by the impl trait, self type, or predicates
--> src/main.rs:7:6
|
7 | impl<'a, T> Index<usize> for Wrap<T>
| ^^ unconstrained lifetime parameter
There are several solutions suggested by the error code E0207, but since we cannot change the Index trait, the only acceptable solution is to make Wrap capture that unconstrained lifetime parameter:
use std::ops::Index;
use std::fmt::Display;
use std::marker::PhantomData;
struct Wrap<'a, T>(Vec<T>, PhantomData<&'a ()>);
// ^~ ^~~~~~~~~~~~~~~~~~~
impl<'a, T> Index<usize> for Wrap<'a, T>
where
T: Display + 'a,
{
type Output = Display + 'a;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
fn main() {
let w = Wrap(vec!['a', 'b'], PhantomData);
println!("{}", &w[0]); // prints "a"
let s = "hi".to_string();
let w = Wrap(vec![&s], PhantomData);
println!("{}", &w[0]); // prints "hi"
}
(Playground)
For sure, this will change your API and that extra lifetime will infect everywhere... If this is not acceptable, you could either
Not use the Index trait, introduce your own lifetime-sensitive trait instead (thus users will need to use w.my_index(i) instead of &w[i]); or
Set Output = Display + 'static, and exclude all transient types. Users will need to clone or use Rc.
Return a reference to T instead of returning a reference to a trait object and let the user cast to a trait object, or just let Rust implicitly infer from the context when to perform the cast:
use std::fmt::Display;
use std::ops::Index;
fn main() {
let w1 = Wrap(vec!['I', 'b']);
let s = "am".to_string();
let w2 = Wrap(vec![&s]);
let w3 = Wrap(vec![1, 2]);
let mut trait_store: Vec<Box<Display>> = Vec::new();
trait_store.push(Box::new(w1.index(0)));
trait_store.push(Box::new(w2.index(0)));
trait_store.push(Box::new(w3.index(0)));
for el in trait_store {
println!("{}", el);
}
}
struct Wrap<T>(Vec<T>);
impl<T> Index<usize> for Wrap<T> {
type Output = T;
fn index(&self, index: usize) -> &Self::Output {
&self.0[index]
}
}
Hi I met the same problem as you. "Like &Index<usize, Output = Display>. This doesn't work with Index directly, but using Index in my question made it a bit easier."
I didn't find out whether the Rust releases some related features or not. But I figure out a rather foolish way to fulfill my demands.
This method only works when the structs which implement the trait are enumerable. Suppose that you have three structs Index1, Index2, Index3, they all implement the trait Index<usize, Output = Display>
Then we can simply wrap these structs by
pub enum Indices{
Index1(Index1),
Index2(Index2),
Index3(Index3),
}
And then implement the trait for the enum and all of its variants, there is an example for this:
rust - How do I implement a trait for an enum and its respective variants? - Stack Overflow

Implementing a trait for closures results in bound/concrete lifetime mismatch

I want to implement a trait for closures of a specific type. Here is a minimal example (playground):
trait Foo {
fn foo(&self, x: &u32);
}
impl<F> Foo for F
where F: Fn(&u32)
{
fn foo(&self, x: &u32) {
self(x)
}
}
fn main() {
let _: &FnOnce(&u32) = &|x| {}; // works
let _: &Foo = &|x| {}; // doesn't work
}
It results in this error:
error: type mismatch resolving `for<'r> <[closure#<anon>:16:29: 16:35] as std::ops::FnOnce<(&'r u32,)>>::Output == ()`:
expected bound lifetime parameter ,
found concrete lifetime [--explain E0271]
--> <anon>:16:28
|>
16 |> let _: &Foo = &|x| {};
|> ^^^^^^^
note: required because of the requirements on the impl of `Foo` for `[closure#<anon>:16:29: 16:35]`
note: required for the cast to the object type `Foo`
error: type mismatch: the type `[closure#<anon>:16:29: 16:35]` implements the trait `std::ops::Fn<(_,)>`, but the trait `for<'r> std::ops::Fn<(&'r u32,)>` is required (expected concrete lifetime, found bound lifetime parameter ) [--explain E0281]
--> <anon>:16:28
|>
16 |> let _: &Foo = &|x| {};
|> ^^^^^^^
note: required because of the requirements on the impl of `Foo` for `[closure#<anon>:16:29: 16:35]`
note: required for the cast to the object type `Foo`
I already tried to explicitly add the HRTB to the where clause like this:
where F: for<'a> Fn(&'a u32)
But it didn't help. I also declared the lifetime on the impl block instead, like this:
impl<'a, F> Foo for F
where F: Fn(&'a u32) { ... }
But this results in a lifetime error within the impl block. I think that those errors are right and the lifetime parameter can't be declared on the impl block.
How can I fix this example?
Check out this part of the error:
[...] implements the trait std::ops::Fn<(_,)>, but the trait for<'r> std::ops::Fn<(&'r u32,)> is required
I think that basically there's not enough code to allow types to be properly inferred. Adding an explicit type annotation allows the example to be compiled:
let _: &Foo = &|x: &u32| {};
Here's a partial answer, starting with an interesting experiment:
trait Foo {
fn foo(&self, x: &u32);
}
impl<F> Foo for F
where F: Fn(&u32)
{
fn foo(&self, x: &u32) {
self(x)
}
}
fn main() {
let f1: &Fn(&u32) = &|_x| {};
let f2: &Foo = &f1;
// but this fails:
// let f3: &Foo = &|_x| {};
f2.foo(&3);
}
(Playground)
All I've done is change the FnOnce to Fn for consistency with the trait, and assign your first closure to a binding of type &Foo - and this one work.
This tells me that the trait itself is fine - it's a problem inferring the type of the closure when making the trait object. Going back to the error, we're told that the closure implements std::ops::Fn<(_,)>, but for<'r> std::ops::Fn<(&'r u32,)> is required. This means that the first thing you tried (adding the for<'r>... to the trait) didn't have any effect because the trait already requires this.
At this point I'm stuck - I don't think I understand the inference rules for closures in enough detail to see either why there's a difference, or how to make it work. I'm hoping someone will come and fill that in!
Disclaimer: I have no idea what I'm doing.
The following works:
trait Foo<'a> {
fn foo(&self, x: &'a u32);
}
impl<'a, F> Foo<'a> for F where F: Fn(&'a u32) {
fn foo(&self, x: &'a u32) {
self(x)
}
}

Resources