如何编写具有相同类型但具有不同生存期的变体(一个是“静态”)的枚举?

时间:2018-07-21 21:23:02

标签: rust

我正在尝试实现一个智能指针,该智能指针应该能够引用类型为T的数据,该数据可以是在生命周期'a借来的,或者是在生命周期'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),
}

SmartPtr还将包含其他枚举字段,例如RcBox(上面的示例已简化)。 SmartPtr将主要用于应该可克隆的结构中……当输入数据T(例如静态字符串)为'static时,我不想克隆数据。

我想为此实现Clone

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

编译器失败:

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),
  |            ^^^^^^^^^^^

我该如何解决?

已编辑

正如我在评论中所写,我的SmartPtr没有任何意义。但这似乎是有道理的:

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
}

我不明白为什么'static中需要SmartPtr<T : 'staticSmartPtr<T : 'static定义了整个枚举的T的生存期,对吧?但是我只需要在Static(&'static T)情况下的静态生存期,而在Rc(Rc<T>)Boxed(Box<T>)的两种情况下都不需要(这是T在Box<T>中拥有的生存期,恕我直言)。

我不明白...但是它有效(但是不知道为什么这样):

#[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);
}

1 个答案:

答案 0 :(得分:0)

感谢trentcl和Shepmaster。这篇 http://stackoverflow.com/questions/40053550/the-compiler-suggests-i-add-a-static-lifetime-because-the-parameter-type-may-no的帖子提供了帮助,现在情况更加清晰了(也许不是100%,但现在足够清晰)。

我举了一个新的例子来说明为什么'static的生存期对于枚举情况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
    }
}