参数类型可能存活时间不够长

4

我有一个简单的程序,其中我正在尝试实现多态账户类型:

enum AccountType {
    INVALID,
    TYPE1,
    TYPE2,
}

trait Account {
    fn get_name(&self) -> String;
    fn get_type(&self) -> AccountType;
}

struct Accounts {
    accounts: Vec<Box<Account>>,
}

impl Accounts {
    fn new() -> Accounts {
        let accs: Vec<Box<Account>> = Vec::new();
        Accounts { accounts: accs }
    }

    fn add_account<A: Account>(&self, account: A) {
        self.accounts.push(Box::new(account));
    }
}

fn main() {
    let accounts = Accounts::new();
}

(Rust Playground)

当我编译它时,会看到以下错误:

error[E0310]: the parameter type `A` may not live long enough
  --> src/main.rs:23:28
   |
22 |     fn add_account<A: Account>(&self, account: A) {
   |                    -- help: consider adding an explicit lifetime bound `A: 'static`...
23 |         self.accounts.push(Box::new(account));
   |                            ^^^^^^^^^^^^^^^^^
   |
note: ...so that the type `A` will meet its required lifetime bounds
  --> src/main.rs:23:28
   |
23 |         self.accounts.push(Box::new(account));
   |                            ^^^^^^^^^^^^^^^^^

我尝试给类型添加生命周期,但是找不到正确的方法。如果这不是在Rust中实现多态的正确方式,请告诉我。

2个回答

11

我将尽力给出更详细的答案:问题与Accountsaccounts成员的定义有关。在此上下文中,Vec<Box<Account>>等同于Vec<Box<Account + 'static>>,即盒子不能包含对堆栈上数据的任何引用。另一方面,add_account的声明不限制类型的生命周期:它等效于fn add_account<'a, A: Account + 'a>(&self, account: A) {

解决方案是确保类型A的寿命足够长。最简单的方法是添加错误消息中建议的A:'static约束(fn add_account<A: Account + 'static>(&self, account: A) {)。

如果您不想复制帐户数据,则可以执行更复杂的操作,例如:

struct Accounts<'a> {
    accounts: Vec<&'a Account + 'a>
}

impl<'a> Accounts<'a> {
    fn new() -> Accounts<'a> {
        Accounts { accounts: Vec::new() }
    }

    fn add_account<A: Account + 'a>(&mut self, account: &'a A) {
        self.accounts.push(Box::new(account));
    }
}

然而,目前为止,您拥有的数据结构可能比您实际需要的更加通用。


2
请问您能详细说明一下在这个上下文中 Vec<Box<Account>> 等同于 Vec<Box<Account + 'static>> 的意思吗?我的意思是,代码中的哪些部分表明它等同于 Vec<Box<Account + 'static>> - soupybionics
4
每个特质对象都有一个关联的生命周期;如果您没有指定生命周期,编译器会使用一些简单的规则来推断它。https://doc.rust-lang.org/book/second-edition/ch19-02-advanced-lifetimes.html#trait-object-lifetimes 提供了一般描述;https://github.com/rust-lang/rfcs/blob/8ee535b4fcc8bb22c121ad19a36414f1259397c0/text/0599-default-object-bound.md 提供了相关原理。 - Eli Friedman
当我添加 'static 时发生了什么? - Crispy13

2
编译器的建议实际上是有效的。如果你像下面这样编写add_account函数:
fn add_account<A: Account + 'static>(&mut self, account: A) {
    self.accounts.push(Box::new(account));
}

你的代码已经编译通过了。(顺便提一下,这里需要使用 &mut self 而不是 &self


6
可能有必要解释一下为什么会出现错误,以及修改后的代码如何修复它。 - Shepmaster
1
@russoue 是的。这个限制的目的是为了防止特质对象超出其引用范围。'static 限制意味着该对象不能有任何非静态引用。 - fjh
1
@russoue,我其实不太确定。通过向“Accounts”添加生命周期参数可能是可行的。我在这方面有点力不从心,而且现在离电脑也比较远,所以无法进行实验。我稍后会尝试一下,并告诉你结果。 - fjh
4
'static并不意味着“一个对象的生存期与程序一样长”。在Trait约束中,'static(或任何其他生命周期约束)意味着该类型不能包含比指定生命周期更短的引用。请注意,引用的缺失也可以满足要求 - 因此,每种不包含内部引用的类型(或者换句话说,没有任何类型参数的类型)都符合任何生命周期约束。 - Vladimir Matveev
哦,我犯了个愚蠢的错误,抱歉。当然,“type parameters”应该读作“lifetime parameters”。 - Vladimir Matveev
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接