2

The value returned from T::bar has the 'static lifetime, so Test2::foo scope doesn't need to own anything. Returning &[T::bar()] as &'static [&'static StructType] should be safe? Test:foo compiles without issue so I was expecting Test2::foo to compile as well.

Code

pub struct StructType {
    a: &'static str,
}

pub trait Foo {
    fn foo() -> &'static [&'static StructType];
    fn bar() -> &'static StructType;
}

pub struct Test;

impl Foo for Test {
    fn foo() -> &'static [&'static StructType] {
        &[&StructType { a: "asdf" }]
    }

    fn bar() -> &'static StructType {
        &StructType { a: "asdf" }
    }
}

pub struct Test2<T: Foo>(T);

impl<T: Foo> Test2<T> {
    pub fn foo() -> &'static [&'static StructType] {
        &[T::bar()]
    }
}

playground

Error

error[E0515]: cannot return reference to temporary value
  --> src/lib.rs:26:9
   |
26 |         &[T::bar()]
   |         ^----------
   |         ||
   |         |temporary value created here
   |         returns a reference to data owned by the current function
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Bryan Chen
  • 45,816
  • 18
  • 112
  • 143
  • 1
    never mind, it's because `T::bar()` is not a constant function so, array is not constant, so the array is construct at runtime. Maybe you can work around with the nightly feature of const function. Doesn't look like trait can have const function, https://github.com/rust-lang/rust/issues/24111 – Stargateur Dec 23 '18 at 23:39
  • @Stargateur I see your point. The array of `Test::foo` only contains constants so it is possible to take a static lifetime reference from it. But for `Test2:foo` it have to evaluate some non-const expression which make the array non const and therefore cannot be taken static lifetime reference from it. Is this right? – Bryan Chen Dec 23 '18 at 23:46
  • that what I think yes, maybe wait some other opinion because I'm not a rust expert yet ;) – Stargateur Dec 23 '18 at 23:50
  • Possible duplicate of [How do I set the return value's lifetime?](https://stackoverflow.com/questions/49581900/how-do-i-set-the-return-values-lifetime) – E_net4 Dec 24 '18 at 00:40

2 Answers2

2

The RFC that added the automatic promotion of references to values to 'static states:

Promote constexpr rvalues to values in static memory instead of stack slots, and expose those in the language by being able to directly create 'static references to them.

Literal values are the most obvious constant expressions. However, a function call is not constant unless explicitly marked as such using const. However, as of Rust 1.31, the types of operations that are available in a user-defined const function are fairly limited. Literal values are allowed:

const fn bar() -> &'static StructType {
    &StructType("asdf")
}

const fn combo() -> &'static [&'static StructType; 1] {
    &[Self::bar()]
}

Converting from a reference to an array to a slice is not allowed in a const function yet, so that needs to be in a different function:

fn wombo() -> &'static [&'static StructType] {
    Self::combo()
}

Additionally, you cannot define const functions in a trait.

See also:

What I really need are 1) have T::bar() return a constant, 2) have Test:foo return an array constant, that is constructed from T::bar() and U::bar() and U, T are generic parameter to Test

You cannot do this

fn example<T>() {
    static NO_CAN_DO: T = unimplemented!();
}
error[E0401]: can't use type parameters from outer function
 --> src/lib.rs:2:23
  |
1 | fn example<T>() {
  |    ------- - type variable from outer function
  |    |
  |    try adding a local type parameter in this method instead
2 |     static NO_CAN_DO: T = unimplemented!();
  |                       ^ use of type variable from outer function

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
0

I think you're thinking about lifetimes the wrong way. It seems like you use them to "declare" how long you want it to live, but you can't change the lifetime of a reference. All the lifetime specifiers do is to help the compiler to understand the lifetime in cases where it doesn't have the information to elide it.

Chapter 15.4.7 Static from Rust by Example should help you out.

Basically, there are only two ways you can create 'static data:

  1. Make a constant with the static declaration.
  2. Make a string literal which has type: &'static str.

You could achieve the same as above by declaring lifetimes specifiers like you normally do in Rust (but the compiler suggested a 'static lifetime since you hadn't declared any lifetimes yourself). See below.

The main point is that, with the exception of &'static str, a lifetime can never be changed by annotating lifetimes in your functions. When you write &[T::bar()], the array is not a constant and will get dropped when you leave the scope if your function. If you want it to live with a 'static lifetime, you need to make it a constant like I show below.

Now that's probably not exactly what you wanted to do but it will compile and I hope explain the difference:

const ARR: &'static [&'static StructType] = &[&StructType { a: "asdf" }];

pub struct StructType {
    a: &'static str,
}

pub trait Foo<'a> {
    fn foo() -> &'a [&'a StructType];
    fn bar() -> &'a StructType;
}

pub struct Test;

impl<'a> Foo<'a> for Test {
    fn foo() -> &'a [&'a StructType] {
        &[&StructType { a: "asdf" }]
    }

    fn bar() -> &'a StructType {
        &StructType { a: "asdf" }
    }
}

pub struct Test2<T: Foo<'static>>(T);

impl<T: Foo<'static>> Test2<T> {
    pub fn foo() -> &'static [&'static StructType] {
        ARR
    }
}
Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
cfs
  • 1,304
  • 12
  • 30
  • Thanks for answering. But unfortunately your example doesn't help much. I know how to make something compiles. But I want to know if it is possible for the current Rust compiler to support my use cases. (i.e. return static reference of an array that contains a result from some method call) – Bryan Chen Dec 24 '18 at 01:05
  • Also I don't see why make a generic lifetime parameter helps at all. All I needs to support is `'static`. – Bryan Chen Dec 24 '18 at 01:08
  • Const functions is soon landing in rust, but I don't think that will make a difference here (if I understand your question correctly). The problem is that the array you create only lives to the end of that function (that memory is freed at that point), even if the array contains static data. So you need to make a static array, and that has to be a constant. The generic lifetimes doesn't help, but in this case declaring them as static don't make any sense either so it was just to seperate the 'statics that served a purpose from those that just as well could be a generic parameter. – cfs Dec 24 '18 at 01:13
  • @BryanChen [one solution](https://play.integer32.com/?version=nightly&mode=debug&edition=2018&gist=d8f51d333a06969bbad224e7d9f1ed1d) could be this at the moment but as you didn't provide why you want this it's hard to me to guess what you need. – Stargateur Dec 24 '18 at 01:20
  • Maybe it's me misunderstanding the question, and if so I'm sorry, but the method call you want to have as static needs to be known at compile time (which it is here since you only store static strings) but you will still need to declare it as a const... If you look here at const functions, maybe that's what you're looking for (still have to be a constant though): https://github.com/rust-lang-nursery/reference/blob/3a7b6d863183079d46d92313df7875e22801de8d/src/items/functions.md#const-functions – cfs Dec 24 '18 at 01:24
  • What I really need are 1) have `T::bar()` return a constant, 2) have `Test:foo` return an array constant, that is constructed from `T::bar()` and `U::bar()` and `U`, `T` are generic parameter to `Test`. I have tried static variable solution and it doesn't work with generic type – Bryan Chen Dec 24 '18 at 01:27
  • I haven't tried it for your case but if that's not working either I don't think your exact problem is so easy to solve the way you want it to right now without changing the design somewhat. Maybe someone else has a solution that I haven't thought of. – cfs Dec 24 '18 at 01:44
  • I can't figure out how to make `lazy_static` works with generic. I don't just have one static variable, I will like to have one per type implements `Foo` trait. – Bryan Chen Dec 24 '18 at 01:44
  • If I can't make this work, I can always adjust the design to workaround it. Thanks for your help anyway. – Bryan Chen Dec 24 '18 at 01:45
  • [Your basic premise about ways to create static data is incorrect](https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=50f5ed4ff8c83bb041ab6fe0e0a86400) – Shepmaster Dec 24 '18 at 14:40
  • @Shepmaster. Thanks for feedback, but can you elaborate where the premise is wrong? My main point was that a lifetime for a non-str type doesn't change if you declare them as 'static. It has no meaning other than telling the compiler you intend for it to live the full lifetime of the program, but the compiler will still stop you if you don't actually live up to your intentions and declare the variable as a const or static variable... – cfs Dec 25 '18 at 15:58
  • I changed the example to not use primitives and add a scope where a "static" lifetime should outlive the scope: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=a3576b29d4f4c4846cf6216c224a081d – cfs Dec 25 '18 at 15:58