2
use std::marker::PhantomData;

struct S<'a, A> {
    _phantom: PhantomData<*const A>,
    value: &'a i32,
}

fn foo<'a, A>(value: &'a i32) {
    let s: &'a S<'a, A> = &S {
        _phantom: PhantomData,
        value,
    };
}

fn main() {}

Struct S actually don't owns generic argument A, so lifetime of A don't relate to lifetime of S. However Rust compiler output error in let s: &'a S<'a, A>:

the parameter type `A` may not live long enough
...so that the reference type `&'a S<'a, A>` does not outlive the data it points at
    consider adding an explicit lifetime bound...: `A: 'a`

Why is lifetime of A bounded? Can we get independent A from the lifetime of S?

(Edit) The above code has another problem about reference to S in foo. More healthy code is:

use std::marker::PhantomData;

struct S<'a, A> where Self: 'a {
    _phantom: PhantomData<*const A>,
    value: &'a i32,
}

fn foo<'a, A>(value: &'a i32) {
    let s: S<'a, A> = S {
        _phantom: PhantomData,
        value,
    };
}

fn main() {}
winjii
  • 63
  • 3
  • Your function `foo` takes an arg `value`. You tell the rust compiler to denote its lifetime by `'a`. As `S` is constructed inside your function, its lifetime is surely smaller than `'a`. But you are asking a reference to that `S` to live for `'a` which isn't possible. – Mihir Luthra Oct 17 '21 at 11:27
  • That is surely problem. As the main topic is that lifetime of `A` is being bounded, I added supplement to the question. – winjii Oct 17 '21 at 11:57
  • Your ask is that `Self: 'a`. If `Self` wants to outlive `'a`, the values inside it shouldn't get invalidated before `'a`. If `A` was `&'b str` such that there is no relation between `'a` and `'b`, `Self` won't be able to outlive `'a`. As per rustc, it is a possibility that `'a` is larger than `'b`. So, it is possible that `*const &'b str` becomes a dangling pointer. Such a situation should not happen in rust without using `unsafe`. – Mihir Luthra Oct 17 '21 at 12:25
  • I understood that `PhantomData<*const A>` bounds lifetime of `A`. What about the case where phantom field is `PhantomData`? In the final, I want to use `A` as not owned by `S` and as having independent lifetime of `S`. – winjii Oct 17 '21 at 12:48
  • I think it makes sense for the compiler to deny it. You want `Self: 'a`. Let's say `A` is `&'b str`. There is no relation between `'a` and `'b` i.e. `'b` maybe smaller. So in your struct you have `_phantom: PhantomData` but `'b` has already expired. If compiler accepts this code, it means that your function pointer wants an arg which should be a reference whose lifetime has already expired. – Mihir Luthra Oct 17 '21 at 13:05
  • I think this is overcomplicating things. A is bound as type, no need to have any A lifetime iiuc. https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=1f919fc0bc203f6009011c842792704c A is not owned, since it has no actual reprensentation in runtime, or does it? – Netwave Oct 17 '21 at 15:14
  • @Netwave I think it has meaning to bound potential lifetime of generic type because generic types may be or contain reference type. – winjii Oct 17 '21 at 16:57
  • @MihirLuthra I see. Since `A` will be binded to the most concrete type, that most concrete type has unique lifetime. And when it expired, `S`'s implementation depending `A` will not make sence. What I want seems to generic type parameter having not unique lifetime i.e. `A` is binded to a type that takes one lifetime argument. In this case, for example, `S` can have generic function pointer takes `A` value having arbitrary lifetime. It may relate to feature called Higher-Kinded Type. I heared of that the feature is not easy to implement in Rust. IIUC, my hope doesn't come true right now. – winjii Oct 17 '21 at 17:11
  • @winjii why not let the compiler elide the necessary lifetime in that case. At least I dont think your statement is valid in your example. Since your generic type is just some phamtom one... – Netwave Oct 17 '21 at 17:14
  • @winjii, this is an interesting question though. I also think this will require higher kinded types([HKT in rust](https://stackoverflow.com/questions/58092746/is-there-an-intrinsic-reason-explaining-why-rust-does-not-have-higher-kinded-typ#:~:text=Rust%20does%20not%20have%20higher%2Dkinded%2Dtypes.,cannot%20be%20written%20in%20Rust.)). I tried giving a few shots with higher rank trait bounds which are available in Rust but no quick solution. It maybe possible but I am not sure about it. Btw, it may help if you change the way of approaching the problem. – Mihir Luthra Oct 17 '21 at 18:07
  • @Netwave Sorry I still don't understand well your thinking. My example is poor and so I put a bit my situation: **1.** `A` is actually used in implementation about `S`, but It is just used, not owned by `S`. (In actual my code, `S` have trait object of `T` that takes a type and its lifetime as argument, and its argument is the type parameter `A` in `S`. Thus `A` is needed but not needed to owned.) **2.** generic type `S` finally has concrete lifetime whether I explicit it. **3.** I wanted `A` to be not bounded by that lifetime, and to have arbitrary lifetime in `S`. – winjii Oct 17 '21 at 18:42
  • 1
    The example you give in the question doesn't make much sense, as `A` is not used at all. Please, show in example how you want to actually use `A` so that we can understand, what you are trying to achieve. So far it seems that you don't need `S` to be generic over `A`. – Maxim Gritsenko Oct 18 '21 at 04:42

1 Answers1

1

[in let s: &'a S<'a, A>] Why is lifetime of A bounded [to 'a]?

When you write &'x T it implies T: 'x and is read as Any type [including reference types] that T contain must be valid during 'x.

In &'a S<'a, A> that basically means:

  • S<'a, A>: 'a which means:
    • &'a i32: 'a => 'a: 'a
    • PhantomData<*const A>: 'a => *const A: 'a => A: 'a.

But when looking at our environment (the fn signature fn foo<'a, A>(value: &'a i32)) the A: 'a is not true hence the error.

Can we get independent A from the lifetime of S?

[Inside of fn foo I suppose?] You can remove 'a lifetime from the reference: let s: &S<'a, A> to get build passing. In this case compiler will use a special unnamed lifetime 'free_region:

let s: &'free_region S<'a, A>

where 'free_region is any lifetime in which S<'a, A> is valid i.e. S<'a, A>: 'free_region which means 'a: 'free_region and A: 'free_region

For better understanding here is another example

Consider adding a second input lifetime 'region:

fn foo<'a, 'region, A>() {
    let s: &'region S<'a, A>;
}

We see that both of lifetimes and A are unrelated to each other. And rustc gives us a reasonable error:

error[E0491]: in type `&'region S<'a, A>`, reference has a longer lifetime than the data it references
 --> src/main.rs:9:12
  |
9 |     let s: &'region S<'a, A>;
  |            ^^^^^^^^^^^^^^^^^
  |
note: the pointer is valid for the lifetime `'region` as defined on the function body at 8:12
 --> src/main.rs:8:12
  |
8 | fn foo<'a, 'region, A>(value: &'a i32) {
  |            ^^^^^^^
note: but the referenced data is only valid for the lifetime `'a` as defined on the function body at 8:8
 --> src/main.rs:8:8
  |
8 | fn foo<'a, 'region, A>(value: &'a i32) {
  |        ^^

We must provide 'a: 'region and A: 'region bounds either in where clauses:

fn foo<'a, 'region, A>()
where
    'a: 'region,
    A: 'region,
    // or just simply
    S<'a, A>: 'region,
{
    let s: &'region S<'a, A>;
}
    

or via RFC 2093: Infer T: 'x outlives requirements on structs

fn foo<'a, 'region, A>(
    _: &'region &'a i32,
    _: &'region PhantomData<*const A>,
    // or just simply
    _: &'region S<'a, A>,
) {
    let s: &'region S<'a, A>;
}
Dawer
  • 309
  • 1
  • 6