1

I'm trying to implement a smart pointer that should be able to reference data of type T either borrowed with the lifetime 'a or borrowed with the lifetime 'static:

pub enum SmartPtr<'a, T: 'a> {
    /// Should live as long as this struct or longer - don't know exactly how long, so we need to clone this when we clone the struct
    Borrowed(&'a T),
    /// Lives forever: So we never need to clone this
    /// Fails here with: 'the parameter type `T` may not live long enough'
    Static(&'static T),
}

The SmartPtr will also contain other enum fields like Rc and Box (the example above has been simplified). SmartPtr will be mostly used in structs that should be cloneable ... when the input data T (for example a static string) is 'static I don't want to clone the data.

I would like to implement Clone for this:

impl<'a, T> Clone for SmartPtr<'a, T>
where
    T: Clone,
{
    fn clone(&self) -> Self {
        match self {
            SmartPtr::Borrowed(value) => {
                // here we have to clone the value
                SmartPtr::Borrowed(value.clone())
            }
            SmartPtr::Static(value) => {
                // Important: No need to clone, since value is static and lives forever
                SmartPtr::Static(value)
            }
        }
    }
}

The compiler fails:

error[E0310]: the parameter type `T` may not live long enough
 --> src/main.rs:7:12
  |
2 | pub enum SmartPtr<'a, T: 'a> {
  |                       -- help: consider adding an explicit lifetime bound `T: 'static`...
...
7 |     Static(&'static T),
  |            ^^^^^^^^^^^
  |
note: ...so that the reference type `&'static T` does not outlive the data it points at
 --> src/main.rs:7:12
  |
7 |     Static(&'static T),
  |            ^^^^^^^^^^^

How do I fix this?

Edited

As I wrote in the comment, my SmartPtr makes no sense. But this seems to make sense:

pub enum SmartPtr<T : 'static /* WHY IS 'static REQUIRED HERE? */> where T : ?Sized {
    Rc(Rc<T>), // Here 'static makes no sense, it's Rc'd
    Boxed(Box<T>), // Here 'static makes no sense, it's owned
    Static(&'static T) // Here we need 'static
}

I don't see why the 'static is required in SmartPtr<T : 'static. The SmartPtr<T : 'static defines the lifetime of T for the entire enum, right? But I only need a static lifetime in case Static(&'static T), not for the two cases Rc(Rc<T>) and Boxed(Box<T>) (T is owned in Box<T> - a lifetime makes no sense IMHO).

I don't get it... but it works (but don't see why this works):

#[test]
fn smart_ptr_test() {
    // Static string -> Ok, this of course works "hello" is static
    let static_string = SmartPtr::Static("hello");

    // Boxed vector -> But it's not 'static, why does this work?
    let vector = vec!["hello"];
    let box_vector = SmartPtr::Boxed(Box::new(vector));

    // Rc'd vector -> why does this work?, vector_rc is not static
    let vector_rc = Rc::new(vec!["hello"]);
    let box_vector = SmartPtr::Rc(vector_rc);
}
SRU
  • 95
  • 6
  • 1
    Just saw my SmartPtr makes no sense, since the cloned SmartPtr will also have a lifetime of 'a. So there's no need to clone T. So there's no need to clone T at all (so no need to have the "Static"-enum-case at all). But still curious: How would an enum look like with 2 cases borrowing the type T - one case with lifetime 'static and one with 'a? – SRU Jul 21 '18 at 21:29
  • The part of your question about cloning seems to have nothing to do with the question you are asking. No errors occur there. What value does it bring to your question? – Shepmaster Jul 21 '18 at 21:41
  • Yes, I just added the 'cloning' to demonstrate my usecase, the actual question is about the two lifetimes 'a and 'static (but the same type T) in the enum for different cases. But as I wrote in my comment: I now see my smart ptr makes no sense at all. – SRU Jul 21 '18 at 22:11
  • You may be confused about the difference between a `&'static` reference and the bound `T: 'static`. [The compiler suggests I add a 'static lifetime because the parameter type may not live long enough, but I don't think that's what I want](http://stackoverflow.com/questions/40053550/the-compiler-suggests-i-add-a-static-lifetime-because-the-parameter-type-may-no) may help explain why `T: 'static` is a meaningful thing to say for types like `Box` and `Rc` – trent Jul 22 '18 at 01:12

1 Answers1

0

Thanks trentcl & Shepmaster. This post http://stackoverflow.com/questions/40053550/the-compiler-suggests-i-add-a-static-lifetime-because-the-parameter-type-may-no helped and things are now clearer (maybe not 100% but clear enough for now).

I made a new example to show why the 'static lifetime makes also sense for the enum case Boxed(Box<T>):

pub enum SmartPtr<T : 'static> where T : ?Sized {
    Rc(Rc<T>),
    Boxed(Box<T>),
    Static(&'static T),
}

struct Bar {
    data: Vec<String>,
}

struct Foo<'a> {
    data: &'a Vec<String>,
}

#[test]
fn smart_ptr_test_2() {
    // Example 1: Does compile: Bar is 'static (since vector is moved)
    {
        let vec2 = vec!["hello".to_string()];
        let bar = Box::new(Bar {
            data: vec2
        });
        let box3 = SmartPtr::Boxed(bar);
    }

    // Example 2: Does not compile ('a in Foo is inferred to be 'static but in my code it's not)
    {
        let vec1 = vec!["hello".to_string()];
        let foo = Box::new(Foo {
            // compiler error: 'note: borrowed value must be valid for the static lifetime'
            data: &vec1
        });
        // when this line is here: the compiler infers lifetime 'a of Foo to be 'static
        let box2 = SmartPtr::Boxed(foo);
        // <--- life of 'vec1' ends here; it's not 'static since 'vec1' goes out of scope here
    }
}
SRU
  • 95
  • 6