Bryan Chen
Bryan Chen

Reputation: 46598

Cannot return a static reference to a temporary array even when the array contains a value that also has the static lifetime

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

Upvotes: 2

Views: 1332

Answers (2)

Shepmaster
Shepmaster

Reputation: 430711

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:

Upvotes: 2

cfs
cfs

Reputation: 1324

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
    }
}

Upvotes: 0

Related Questions