Assigning proper lifetimes to associated types - rust

I am designing code that should be generic over a trait Atom, that contains many associated types that logically belong together. In the simplified example below, which concerns representing a mathematical expression, there is one variant that gives a 'view' of a Pow and an owned version of a Pow. In actuality, there will be many more variants, such as Var and OwnedVar, Function and OwnedFunction, etc.
Since some traits deal with borrowed data, I added lifetimes and this makes the code below not compile:
pub trait Atom {
// imagine more variants here
type P<'a>: Pow<'a, R = Self>;
type OP: OwnedPow<R = Self>;
}
pub trait Pow<'a> {
type R: Atom;
fn get_base(&self) -> AtomView<Self::R>;
}
pub trait OwnedPow {
type R: Atom;
fn to_view<'a>(&'a self) -> AtomView<Self::R>;
}
pub enum AtomView<'a, R: Atom> {
Pow(R::P<'a>),
}
impl<'a, R: Atom> AtomView<'a, R> {
#[allow(unconditional_recursion, irrefutable_let_patterns)]
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
if let AtomView::Pow(p2) = other {
let b = p2.get_base();
Some(self.partial_cmp(&b).unwrap())
} else {
None
}
}
}
// concrete implementations
struct Rep;
impl Atom for Rep {
type P<'a> = PowD<'a>;
type OP = OwnedPowD;
}
struct PowD<'a> {
data: &'a [u8],
}
impl<'a> Pow<'a> for PowD<'a> {
type R = Rep;
fn get_base(&self) -> AtomView<Self::R> {
AtomView::Pow(PowD {
data: &self.data[1..],
})
}
}
struct OwnedPowD {
data: Vec<u8>,
}
impl OwnedPow for OwnedPowD {
type R = Rep;
fn to_view<'a>(&'a self) -> AtomView<Rep> {
AtomView::Pow(PowD { data: &self.data })
}
}
fn test<A: OwnedPow>(a: A) {
let _vv = a.to_view();
}
fn main() {
let v = OwnedPowD {
data: vec![1, 2, 3],
};
test(v);
}
This code does not compile:
error[E0621]: explicit lifetime required in the type of `other`
--> src/main.rs:25:21
|
23 | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
| ----- help: add explicit lifetime `'a` to the type of `other`: `&'a AtomView<'a, R>`
24 | if let AtomView::Pow(p2) = other {
25 | let b = p2.get_base();
| ^^^^^^^^^^^^^ lifetime `'a` required
Of course, giving other the lifetime a is a problem, since other is a locally created variable and thus following the help will not fix the problem. I fail to see why &b seems to have the lifetime &'a b.
The code can be tested on the playground.
I tried fixing this code by dropping many lifetimes in the associated types and traits, but then it seems I need to give Rep a lifetime, and then this Rep<'a> will make its way into OwnedPowD that should have no lifetime (since the data is owned). This attempt can be tried here.
Does anyone know how to assign proper lifetimes?

AtomView has a lifetime argument, which should connect all of the borrowing dependencies together. But you are never specifying it!
pub enum AtomView<'a, R: Atom> {
Pow(R::P<'a>),
}
I was able to make your code compile by changing the trait methods to constrain this lifetime in the "obvious" way in each case:
pub trait Pow<'a> {
type R: Atom;
fn get_base(&self) -> AtomView<'a, Self::R>;
}
pub trait OwnedPow {
type R: Atom;
fn to_view(&self) -> AtomView<'_, Self::R>;
}
And updating the implementations to match:
impl<'a> Pow<'a> for PowD<'a> {
type R = Rep;
fn get_base(&self) -> AtomView<'a, Self::R> {
// just for testing
AtomView::Pow(PowD {
data: &self.data[1..],
})
}
}
impl OwnedPow for OwnedPowD {
type R = Rep;
fn to_view(&self) -> AtomView<'_, Rep> {
AtomView::Pow(PowD { data: &self.data })
}
}

Related

Lifetimes on traits as return values

I would like to hide the actual implementation that is returned from the create function by returning an impl trait as in create_trait(). How could this be done?
trait Names<'a> {
fn names(&'a self) -> &Vec<&'a str>;
}
struct NamesImpl<'b> {
names: Vec<&'b str>,
}
impl<'c> Names<'c> for NamesImpl<'c> {
fn names(&'c self) -> &Vec<&'c str> {
&self.names
}
}
fn create_impl<'i>() -> NamesImpl<'i> {
NamesImpl {
names: vec!["Hello", "world"],
}
}
#[allow(dead_code)]
fn create_trait<'t>() -> impl Names<'t> {
NamesImpl {
names: vec!["Hello", "world"],
}
}
fn main() {
let names_impl = create_impl();
println!("Names: {:?}", names_impl.names());
//This doesn't compile, see error below
let names_trait = create_trait();
println!("Names: {:?}", names_trait.names());
}
I can't wrap my head around the following compile error. Why does it work with create_impl() but not with create_trait()?
error[E0597]: `names_trait` does not live long enough
--> src/main.rs:34:29
|
34 | println!("Names: {:?}", names_trait.names());
| ^^^^^^^^^^^ borrowed value does not live long enough
35 | }
| -
| |
| `names_trait` dropped here while still borrowed
| borrow might be used here, when `names_trait` is dropped and runs the destructor for type `impl Names<'_>`
A general rule of thumb when it comes to using lifetimes in Rust is that if you don't know what you're doing, let the compiler infer the correct lifetimes for you. Once you get more experience, you'll learn when you actually need to use explicit lifetime annotations, but your example is not one of those situations. I was able make it compile by removing all the unnecessary lifetime annotations:
trait Names {
fn names(&self) -> &Vec<&str>;
}
struct NamesImpl<'a> {
names: Vec<&'a str>,
}
impl Names for NamesImpl<'_> {
fn names(&self) -> &Vec<&str> {
&self.names
}
}
fn create_impl() -> NamesImpl<'static> {
NamesImpl { names: vec!["Hello", "world"] }
}
fn create_trait() -> impl Names {
NamesImpl { names: vec!["Hello", "world"] }
}
fn main() {
let names_impl = create_impl();
println!("Names: {:?}", names_impl.names());
// compiles
let names_trait = create_trait();
println!("Names: {:?}", names_trait.names());
}
playground
To determine which lifetime annotations were unnecessary, I started by removing all of them and then only added lifetime annotations back to the areas where the compiler specifically asked me to.
This section in Common Rust Lifetime Misconceptions (Thank you pretzelhammer!) was an eye opener to me. It made mee see that in this construction
trait Names<'a> {
fn names(&'a self) -> &Vec<&'a str>;
}
calling names() will borrow the struct for the rest of its lifetime.
Correcting this in the original code with explicit lifetime annotations is possible, but the result is far from beautiful. I just tried this in an attempt to better understand the 'why'.
trait Names<'a : 'f, 'f> {
fn names(&self) -> &Vec<&'f str>;
}
struct NamesImpl<'b> {
names: Vec<&'b str>,
}
impl <'c : 'f, 'f> Names<'c, 'f> for NamesImpl<'c> {
fn names(&self) -> &Vec<&'f str> {
&self.names
}
}
fn create_impl<'i : 'f, 'f>() -> NamesImpl<'i> {
NamesImpl{names: vec!["Hello", "world"]}
}
#[allow(dead_code)]
fn create_trait<'t : 'f, 'f>() -> impl Names<'t, 'f> {
NamesImpl{names: vec!["Hello", "world"]}
}
fn main() {
let names_impl = create_impl();
println!("Names: {:?}", names_impl.names());
//This compiles
let names_trait = create_trait();
println!("Names: {:?}", names_trait.names());
}
Conclusion: Follow the advice given by Shepmaster

rust says trait From<i32> isn't implimented

I've got an enum:
#[derive(PartialEq, Debug)]
pub enum EventValue {
Numeric(i32),
Bool(bool),
Text(String),
}
And it's used in a Vec which is used in a HashMap:
type Events = Vec<Event>;
pub type Stream = HashMap<String, Events>;
and I've implemented the From trait on it for i32 (and the other three types):
impl From<i32> for EventValue {
fn from(v: i32) -> Self {
EventValue::Numeric(v)
}
}
impl From<String> for EventValue {
fn from(v: String) -> Self {
EventValue::Text(v)
}
}
impl From<bool> for EventValue {
fn from(v: bool) -> Self {
EventValue::Bool(v)
}
}
but when I try to use it in a function:
let motions = event_stream.get_channel("motions"); // << return a Vec
for motion in motions.drain(0..) {
let amount: i32 = motion.value.into(); // <-- here is where I get the error
// .. do somthing with amount
}
I get this error:
the trait bound `i32: std::convert::From<prelude::game::entity::components::event_stream::EventValue>` is not satisfied
--> src/game/system/entity/movement.rs:17:48
|
17 | let amount: i32 = motion.value.into();
| ^^^^ the trait `std::convert::From<prelude::game::entity::components::event_stream::EventValue>` is not implemented for `i32`
|
= help: the following implementations were found:
<i32 as std::convert::From<bool>>
<i32 as std::convert::From<i16>>
<i32 as std::convert::From<i8>>
<i32 as std::convert::From<std::num::NonZeroI32>>
and 2 others
= note: required because of the requirements on the impl of `std::convert::Into<i32>` for `prelude::game::entity::components::event_stream::EventValue`
What have I missed?
Bonus:
It's possible to build a function that auto converts incoming values for you provided you implement From like this:
impl Event {
pub fn new<V: Into<EventValue>>(message: String, value: V) -> Event {
Self {
message: message,
value: value.into(),
}
}
}
Is it possible to create a function that can do the same thing but for returning a value?
The error says that From<EventValue> isn't implemented for i32, not that From<i32> isn't implemented for EventValue. With the fully qualified names, it's a little harder to read, but that's what
the trait bound i32: std::convert::From<prelude::game::entity::components::event_stream::EventValue> is not satisfied
is saying.
The problem is that you're going the wrong direction. You've implemented the conversion i32 -> EventValue, but not EventValue -> i32, which is what your sample code is trying to do.
You'll probably want to match on the value instead and handle not only the Numeric case, but also the Bool and Text cases.
let motions = event_stream.get_channel("motions"); // << return a Vec
for motion in motions.drain(0..) {
match motion.value {
Numeric(value) => {// handle `Numeric` case},
Bool(value) => {// handle `Bool` case},
Text(text) => {// handle `Text` case},
}
}
motion.value could be any of those three variants, so you can't assume it'll always be convertible to i32.

Why must the associated type be specified in a collection of trait object references?

Here is an offending example (Playground):
// Some traits
trait Behaviour {
type Sub: SubBehaviour;
}
trait SubBehaviour {}
// Some implementations of these traits
struct A;
impl Behaviour for A {
type Sub = B;
}
struct B;
impl SubBehaviour for B {}
// Struct that holds a collection of these traits.
struct Example<'a> {
behaviours: Vec<&'a dyn Behaviour>,
}
impl<'a> Example<'a> {
fn add_behaviour<T: Behaviour>(&mut self, b: &'a T) {
self.behaviours.push(b);
}
}
fn main() {
let b = A;
let mut e = Example {
behaviours: Vec::new(),
};
e.add_behaviour(&b);
}
I get:
error[E0191]: the value of the associated type `Sub` (from trait `Behaviour`) must be specified
--> src/main.rs:17:29
|
3 | type Sub: SubBehaviour;
| ----------------------- `Sub` defined here
...
17 | behaviours: Vec<&'a dyn Behaviour>,
| ^^^^^^^^^ help: specify the associated type: `Behaviour<Sub = Type>`
Why must this type must be specified, particularly in this case where we are only storing a reference to the object? How can I get this code to work?
All types must be statically known at compile time. If Rust would allow different associated types for elements of a Vec, type information could depend on indices which are only known at runtime.
I find it helpful to consider a smaller example:
trait Behaviour {
type T;
fn make_t(&self) -> T;
}
fn foo(my_vec: Vec<&dyn Behaviour>, index: usize) {
let t = my_vec[index].make_t(); //Type of t depends on index
}
You were on the right track to fixing this though. I assume you introduced the SubBehaviour trait because you realized you need to put restrictions of what T can be. The thing is, in that case you don't need an associated type anymore.
trait SubBehaviour {}
trait Behaviour {
fn make_t(&self) -> Box<dyn SubBehaviour>;
fn ref_t(&self) -> &dyn SubBehaviour; // also fine
}
fn some_function(my_vec: Vec<&dyn Behaviour>, index: usize) {
let t1 = my_vec[index].make_t();
}
The only limitation is that in your definition of Behaviour you can not do anything which would depend on the size of T, (like allocating it on the stack or moving it) since the size of T can not be specified by the SubBehaviour trait.
You need to specify the associated type of the trait (i.e. Behavior<Sub = ???>).
When adding the associated type at all places, it compiles:
struct Example<'a, S: SubBehaviour + 'a> {
behaviours: Vec<&'a Behaviour<Sub = S>>,
}
impl<'a, S: SubBehaviour> Example<'a, S> {
fn add_behaviour<T: Behaviour<Sub = S>>(&mut self, b: &'a T) {
self.behaviours.push(b);
}
}
See this in action on the Playground
So the answer to your first question is covered by Tim's answer and is correct, you might not want your Example to be generic. In that case, you need to use some sort of type erasure:
// Some traits
trait Behaviour {
type Sub: SubBehaviour;
}
trait SubBehaviour {}
// Some implementations of these traits
struct A;
impl Behaviour for A {
type Sub = B;
}
struct B;
impl SubBehaviour for B {}
struct AnyBehaviour {
closure: Box<Fn()>,
}
impl AnyBehaviour {
fn new<U: SubBehaviour, T: Behaviour<Sub = U>>(b: &T) -> Self {
let closure = || {
//let sub = T::Sub::new();
println!("Can use T here");
};
AnyBehaviour {
closure: Box::new(closure),
}
}
}
// Struct that holds a collection of these traits.
struct Example {
behaviours: Vec<AnyBehaviour>,
}
impl Example {
fn add_behaviour<U: SubBehaviour, T: Behaviour<Sub = U>>(&mut self, b: &T) {
self.behaviours.push(AnyBehaviour::new(b));
}
}
fn main() {
let b = A;
let mut e = Example {
behaviours: Vec::new(),
};
e.add_behaviour(&b);
}
Within the closure, you have access to all the types needed call the traits functions with whatever subtype needed.
Why this happens, is mostly because you actually need a definition of the associated type in order for the trait to be "complete" so the compiler can work with it. Tim's answer answers that by the definition to be higher up in the chain (outside of Example) instead of inside.

Using a borrow as an associated trait type

This code works:
struct Test {
val: String,
}
impl Test {
fn mut_out(&mut self) -> &String {
self.val = String::from("something");
&self.val
}
}
However, a more generic implementation does not work:
struct Test {
val: String,
}
trait MutateOut {
type Out;
fn mut_out(&mut self) -> Self::Out;
}
impl MutateOut for Test {
type Out = &String;
fn mut_out(&mut self) -> Self::Out {
self.val = String::from("something");
&self.val
}
}
The compiler cannot infer a lifetime for the string borrow:
error[E0106]: missing lifetime specifier
--> src/main.rs:13:16
|
11 | type Out = &String;
| ^ expected lifetime parameter
I cannot figure out a way to explicitly state the lifetime of the borrow, as it depends on the function itself.
Taking inspiration from the Deref trait, you can remove the reference from the associated type and instead just note in the trait that you want to return a reference to the associated type:
trait MutateOut {
type Out;
fn mut_out(&mut self) -> &Self::Out;
}
impl MutateOut for Test {
type Out = String;
fn mut_out(&mut self) -> &Self::Out {
self.val = String::from("something");
&self.val
}
}
Here it is in the playground. Given that your function name was mut_out, if a mutable reference is what you were after, here is a playground example with that as well.

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.

Resources