I need help in this contradiction

(Sorry if its clickbaity; I not good at “titling” the post)

Look more down as such there more specific example what I meant; if your confused on this example

How do you fix this?

For example

trait Example {
    type Whatever;
    fn etc() -> Self::Whatever;
}
struct UnitValue;
impl<'a> Example for UnitValue {
    type Whatever = &'a UnitValue;
    // Assuming you can't do 'static lifetime onto this type
    fn etc() -> Self::Whatever {
        &UnitValue
    }
}

Assuming the etc() function uses a reference OF SELF as input and outputs a reference OF ITS OWN (e.g &type.field, etc) OR SOME SORT (&’static 5, etc), AND cannot change the trait (for example from a lib)
This errors says ‘a is unconstrained


trait Example {
    type Whatever;
    fn etc() -> Self::Whatever;
}
struct UnitValue;
impl Example for UnitValue {
    type Whatever<'a> = &'a UnitValue;
    // Assuming you can't do 'static lifetime onto this type
    fn etc() -> Self::Whatever {
        &UnitValue
    }
}

This doesn’t work as such conflicts with the trait implementation

Can somebody help please?

etc taking a reference as input and output would be significant, since the lifetime of the output could depend on the lifetime of the input. If etc takes no inputs and is a method of a type with no generics implemented on a 'static type, then 'static is the only possible lifetime in the output.

Any lifetime in the output must either be 'static or come from one of the following sources:

  • the type the method is implemented on (e.g. Cow<'a, str> has a 'a lifetime, while u32 has none)
  • generic parameters on the trait
  • inputs to the method (including the 'a in &'a self or '_ in &self, above and beyond any lifetime in the type itself)

I don't think I've forgotten any other lifetime sources.

Sorry if last bit was ambiguous I meant as in assuming
fn etc(&’a self) -> Self::Whatever; yes that one you mentioned at last point, not a generic form but can you fix it?

Or am I being incompetent

Ultimately, whose reference's lifetime is the etc() class method supposed to return other than 'static? For instance, we could have:

let a = UnitValue;
let b = UnitValue;

both implementing the Example trait, but with different locations. But the etc() class method doesn't take &self so the compiler is at a loss about where that reference is supposed to come from since it can't conjure one from thin air.

What do you mean?
But from my understanding I know referencing unit structs have static lifetimes (like you probably said from my interpretation) but I’m more focusing on error on the Type Whatever = &sometype, the thing is when you have type with a (or more) reference then its need a lifetime but the problem is considering the issue with that I can’t put &’a sometype without an <‘a> (to introduce it) see 2 what I tried to fix above. Sorry, if the example is still confusing (and assumptions)

What about implementing the trait on &'a StaticType, and having Whatever then be Self?

Show me?

If your confused I use different example what I mean more specific like here

struct  Buffer {
    buffer: [u8; 10],
}
impl Iterator for Buffer {
    type Item = & /* expects lifetime but Item<'a> doesn't work or using impl<'a> */ u8;
    fn next(&mut self) -> Option<<Self as Iterator>::Item> {
        Some(&self.buffer[0])
        // This implementation is just an example
    }
    
}

Edit: Your problem is clear now that you've added the Iterator example. See @robofinch's reply below; you can

  • Not use Iterator
  • Have an iterator type that holds some borrow
    • perhaps a reference in a field, but perhaps you can just wrap some other borrowing iterator
  • Have an iterator that hands out owned values instead of references
Expand for my original comment

Is your problem literally that you are working with some trait that has this shape (which you cannot change)...

// No parameters on the trait
trait Example {
    // No parameters on the associated type
    type Whatever;
    // No parameters and no arguments on the associated function
    fn etc() -> Self::Whatever;
}

...and you desire this particular implementation (and not e.g. impl Example for &UnitValue)?

// No parameters on the type
struct UnitValue;

impl Example for UnitValue {
    // Not acceptable to use `'static`
    type Whatever = &'??? UnitValue;
    ...
}

If so, there is no solution; something has to give (change the trait, change the implementing type, change the associated type to use 'static or not have a lifetime).

1 Like

Ahhhhhhh, that is a case where you'd want a "lending iterator", if anything, if you absolutely need to return a reference from an owned iterator like that. (It requires a different trait, not std's Iterator.) But look at what std's iterators do: iterators that own a collection return owned items, and iterators that borrow a collection return borrowed or owned items.

So, here, I really would try to implement an iterator referencing a Buffer rather than owning one. In particular, I'd probably implement IntoIterator for &'a Buffer, and add an iter(&self) method to Buffer which calls self.into_iter() (note that this would use the implementation of IntoIterator for &'_ Buffer).

And I say IntoIterator instead of Iterator because an iterator would also need to keep track of how far into iteration it is, while a base Buffer not being used as an iterator would not need to care about a cursor. Keeping a distinction between collections that can be iterated over and things which do the iteration is very useful.

3 Likes

Oh. So this is impossible with the iterator trait i am implementing on but using IntoIterator as your suggestion works. Little confused but show me example to clarify your point please.

Hello?.

struct Buffer {
    buffer: [u8; 10],
}

struct Iter<'a> {
    inner: std::slice::Iter<'a, u8>,
}

impl<'a> Iterator for Iter<'a> {
    type Item = &'a u8;
    fn next(&mut self) -> Option<Self::Item> {
        self.inner.next()
    }
}

impl<'a> IntoIterator for &'a Buffer {
    type Item = &'a u8;
    type IntoIter = Iter<'a>;
    fn into_iter(self) -> Self::IntoIter {
        Iter {
            inner: self.buffer.iter()
        }
    }
}
1 Like

Thanks for letting me understand also,

Ooh, but I modified your answer in rust playground as there was the same “type = &’a sometype” except your example compiled seeing at the end there was

impl Trait for &’a Sometype {
     type _name = &’a Sometype
}

meaning this bit was allowing to compile if such cases type _name needs an reference (and lifetime after),

Also thank you for letting me understand how implement iterators and applying lifetime to type _name = &’a sometype,

Technically 2 posts are the solution to my problem but the solution button assumes 1 post did it so if your wondering why there no solution “filed” here why

If you want to see how an iterator might be implemented (instead of deferring to an inner iterator, which is very much a good option when possible):

struct Buffer {
    buffer: [u8; 10],
}

struct Iter<'a> {
    buffer: &'a Buffer,
    index:  u8,
}

impl<'a> Iterator for Iter<'a> {
    type Item = &'a u8;
    fn next(&mut self) -> Option<Self::Item> {
        if let Some(next) = self.buffer.buffer.get(usize::from(self.index)) {
            self.index += 1;
            Some(next)
        } else {
            None
        }
    }
}

impl<'a> IntoIterator for &'a Buffer {
    type Item = &'a u8;
    type IntoIter = Iter<'a>;
    fn into_iter(self) -> Self::IntoIter {
        Iter {
            buffer: self,
            index:  0,
        }
    }
}

Edit: index is a dumb name. next_index or maybe next_idx would be better.

2 Likes

May I ask a question?

How you know rust alot

There can be no reply if you want though

I learned programming in high school by learning JavaScript, on the basis that I could quickly get visible feedback with HTML/CSS/JavaScript. (JS sucks. It has silent errors all the time, so I would not necessarily recommend starting with that.) I later learned a fair amount of Java by porting a JS project of mine to Java. Then, after getting interested in Rust, I tried porting the project to Rust. It was fun to write the same project in multiple languages like that, since I already knew what I was trying to do, and was merely trying to do it better/faster/with less memory. If you have anything like that, it might be a fun idea.

The first ten thousand lines of Rust I wrote, including all the code of that project, were surely awful. (Even if I didn't realize at the time how bad my code was.)

But I loved the experience of using the language. Rust felt friendlier / easier to write. It probably helps that by the time I started learning Rust, my college classes had covered C, and at some point I had courses on systems programming and operating systems. All the conceptual lessons still apply in Rust, even if some of the details (like using linked lists in C because implementing dynamic arrays is a hassle) are different. And I also really love math; I think the proof-based reasoning used in math is also very useful for Rust.

Because I was interested in Rust, at some point I then spent hours upon hours upon hours reading about Rust and watching videos about Rust. I read lots of Rust blogs, Rust documentation, RFC's, issues on the rust-lang/rust GitHub, and watch some videos. And, of course, I write Rust.

I started with what I consider the "happy path" (or perhaps "happiest path" considering how limited it is): no macros, no unsafe, no generics, no async. Just concrete types and traits, code that's computing things and displaying some simple outputs. Later I learned how to use generics. Maybe after a year and a half I finally started to use macros. And after that I learned how to use simple unsafe. And in the process of reading enormous amounts of information about unsafe Rust (I am perhaps slightly paranoid, and do not want to make mistakes), I've gotten better at understanding how the Rust compiler works and sees the programs we write.

I still haven't really messed with nightly features or async, so I still have more Rust to learn. It just takes time and experience, I think. Like, I could look back on the code I wrote six months ago, and berate past-me for being an absolute idiot. As with no doubt many things you could learn, learning Rust primarily requires writing a lot of Rust. You'll get better in time.

1 Like

Aaah, make sense, the experience you gain and in your point of view the friendly compiler reinforces that when introducing rust that became your passion, You started using concrete types then later develop an intuition to generics and beyond

I got into rust because well I don’t remember much why but you do so that good and I do not mean to curse you (not swearing, some people believe that saying good attribute to somebody may like accidentally or purposely (due to jealousy) “revokes” the good attributes, I know this explanation is bad, but generally I mean no harm)

I got another question why does this work

impl<‘a> Trait for &’a SomeType {
    type _name = &’a SomeType
    // etc with implementation
}

But with this

impl<‘a> /*Wouldn’t work due being “unconstrained”*/
// Why this wouldn’t work of impl<‘a> why is treated as unconstrained
// Why would adding lifetime to type to trait implementation is not allowed
// (unless type in the trait is allowed like Type Example<‘a>)
Trait for /* No Reference With Lifetime (e.g &’a) */ SomeType {
    type _name/*<‘a> this wouldn’t work either*/= &’a SomeType
    // etc with implementation
}

You can only impl Trait for SomeType a single time. However, since 'a is not constrained by either Trait or SomeType, that single impl block is implementing the exact same trait multiple times for the exact same type. I think that's the rationale. But &'a SomeType is a different type for each different lifetime 'a, so you aren't creating overlapping implementations of Trait.

Vec<u32> and Vec<String> are different types. Or in generic terms, Vec<T> and Vec<U> are only the same type when T = U. There is also no Vec<..>[1] type that means "a Vec<_> with any element type" in Rust (there are no higher kinded types).

Similarly, &'a SomeType and &'b SomeType are only the same type when 'a = 'b,[2] and there is no &'.. SomeType that means "a reference with any lifetime".

Types with parameters (be they lifetimes or types) are sometimes called "type constructors". You have to specify concrete "values" (lifetimes, types) to the parameters to get an actual type.

With that in mind, this:

<ConcreteType as TraitWith<Concrete, Params>>::AssociatedTypeWithNoParams

is always a single type. You can't add parameters to the associated type as an implementor; that would make it mean something different at the type system level. So, no _name<'a>.


If AssociatedTypeWithNoParams is a type with a non-'static lifetime in it, it has to be a lifetime that's present in the implementing type or the parameters of the trait...

 Only generics present in these places.....    ..........can be used here
 vvvvvvvvvvvv              vvvvvvvv  vvvvvv    vvvvvvvvvvvvvvvvvvvvvvvvvv
<ConcreteType as TraitWith<Concrete, Params>>::AssociatedTypeWithNoParams

...because the implementation is unique given the implementing type and trait with concrete parameters. If a non-'static lifetime were allowed, it would be unconstrained (would have to be pulled out of thin air), which is generally unsound.

Moreover, the borrow checker exploits the "no unconstrained parameters" property:

use std::any::Any;

// This compiles which is only possible if `Iter::Item: 'static`
fn example<Iter>(mut iter: Iter) -> Box<dyn Any + 'static>
where
    Iter: Iterator + 'static,
{
    Box::new(iter.next())
}

If the implementor meets a 'static bound, and all the trait parameters meet a 'static bound, than the only lifetime that is nameable in the (unparameterized) associated type is 'static -- which in turn means the associated type meets a 'static bound.

We required Iter: 'static and Iterator has no trait parameters, so the compiler (and code downstream from the implementation) can conclude and exploit <Iter as Iterator>::Item: 'static too.

So, no _name = &'non_static_lifetime_unseen_until_now SomeType.


Alternatively, we can think of it in terms of the method signature. As a reminder, the meaning of signatures like these:[3]

    fn foo(&mut self) -> &mut Vec<u32>;
    // same thing
    // fn foo<'s>(&'s mut self) -> &'s mut Vec<u32>

    fn bar(&mut self) -> Self::GenericAssociatedType<'_>;
    // same thing
    fn bar<'s>(&'s mut self) -> Self::GenericAssociatedType<'s>;

is "the output is borrowing via the input", so "using the return type keeps the borrow of *self active". That's the property you would need to return some slice of Buffer::buffer on a Buffer method that took &mut self. Because without that borrowing relationship, you could for example move the Buffer without invalidating the returned reference -- but that means the reference is now dangling.

Going back to the std::iter::Iterator signature:

    // No paramter on the associated type
    type Item;
    //                           So no free paramters over here
    fn next<'s>(&'s mut self) -> Option<Self::Item>

There is no way for the lifetime 's to show up in the return type, which is what would be required to soundly return a reference to some field of Self. This is why you can drop an iterator[4] without invalidating the items you got out of it. For example that's why you can collect() any iterator (which consumes the iterator but leaves you the items).

And (once you have internalized enough of the type system) you can tell this is the case from the method signature, because there's no borrowing relationship between the &mut self and the return type.

In other words, whenever you have a signature like this:

    fn method(&mut self) -> Vec<u32>
    // Or any other return type with no free lifetime parameters

The borrow of *self passed in to the method ends as soon as the method returns -- there is no way for the returned value to keep the borrow active, because that requires some lifetime relationship. Namely a relationship with &'_ mut self, and there's no where in the return type to put that lifetime.

In the case of traits like Iterator, that can be considered part of the trait contract which all downstream code can rely on (and thus which all implementors must adhere to).


  1. or however you want to "spell" it ↩︎

  2. 'a: 'b and 'b: 'a ↩︎

  3. where the input type and output type have the same lifetime ↩︎

  4. I.e. a std::iter::Iterator implementing type ↩︎

Okay

I understand that now but What if BadTrait did compile, you said that it be unsound can you give an example that hypothetically lead to memory issue

Also what would differ from Example Trait and Monomorphised Trait from this trait and what effects does adding a lifetime parameter to this trait

trait LifeType {
    type Life<'a>;
}

impl LifeType for u8 {
    type Life<'a> = &'a str;
}
type Reffy<'a> = <u8 as LifeType<'a>>::Life;