为什么 Vec.sort() 看起来需要静态生命周期?

3
这是一个极为简化的例子,我的问题是:给定一个实现了 Ordtrait Thing 和一个实现了 Thingstruct Object,我有以下结构体:
pub struct MyStruct<'a> {
    my_things: HashMap<i32, Vec<Box<dyn Thing + 'a>>>
}

impl<'a> MyStruct<'a> {
    pub fn new() -> MyStruct<'a> {
        MyStruct {
            my_things: HashMap::new()
        }
    }
    
    pub fn add_object(&mut self, key: i32, obj: Object) {
        if !self.my_things.contains_key(&key) {
            self.my_things.insert(key, Vec::new());
        }
        
        let new_thing: Box<dyn Thing> = Box::new(obj);
        let things = self.my_things.get_mut(&key).unwrap();
        
        things.push(new_thing);
        things.sort();
    }
}

这个函数接受一个键和一个Object,并使用给定的键将对象添加到VecHashMap中。我知道这不是最优的方法,但为了说明问题,我想保持简单。

在调用things.sort()时,编译器报错,错误如下:

error[E0308]: mismatched types
  --> src/main.rs:58:16
   |
58 |         things.sort();
   |                ^^^^ lifetime mismatch
   |
   = note: expected trait `Ord`
              found trait `Ord`
note: the lifetime `'a` as defined on the impl at 42:6...
  --> src/main.rs:42:6
   |
42 | impl<'a> MyStruct<'a> {
   |      ^^
   = note: ...does not necessarily outlive the static lifetime

Playground链接

如果我在这个例子中移除所有的 'a 生命周期,那么代码将会编译通过。但是对于我的实际使用情况,我需要允许非静态生命周期。

有人能解释一下发生了什么吗?sort() 真的需要 Vec 包含具有静态生命周期的项吗? 如果是这样,为什么?

有好的解决方法吗?

1个回答

6

您只为dyn Thing + 'static实现了Ord。如果您没有显式指定其他生命周期界限,则会推断出'static生命周期界限。要为非'staticdyn Thing实现Ord,您需要引入并在实现中使用一个通用的生命周期参数,例如'a。更新的编译示例:

use std::collections::HashMap;
use std::cmp;

pub trait Thing {
    fn priority(&self) -> i32;
}

impl<'a> PartialEq for dyn Thing + 'a { // 'a added here
    fn eq(&self, other: &Self) -> bool {
        self.priority() == other.priority()
    }
}

impl<'a> PartialOrd for dyn Thing + 'a { // 'a added here
    fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
        self.priority().partial_cmp(&other.priority())
    }
}

impl<'a> Eq for dyn Thing + 'a {} // 'a added here

impl<'a> Ord for dyn Thing + 'a { // 'a added here
    fn cmp(&self, other: &Self) -> cmp::Ordering{
        self.priority().cmp(&other.priority())
    }
}

pub struct Object {
    priority: i32,
}

impl Thing for Object {
    fn priority(&self) -> i32 {
        self.priority
    }
}

pub struct MyStruct<'a> {
    my_things: HashMap<i32, Vec<Box<dyn Thing + 'a>>>
}

impl<'a> MyStruct<'a> {
    pub fn new() -> MyStruct<'a> {
        MyStruct {
            my_things: HashMap::new()
        }
    }
    
    pub fn add_object(&mut self, key: i32, obj: Object) {
        if !self.my_things.contains_key(&key) {
            self.my_things.insert(key, Vec::new());
        }
        
        let new_thing: Box<dyn Thing> = Box::new(obj);
        let things = self.my_things.get_mut(&key).unwrap();
        
        things.push(new_thing);
        things.sort();
    }
}

fn main() {
    let _test = MyStruct::new();
}

playground


以下是来自于Rust参考手册中Lifetime Elision章节的Default Trait Object Lifetimes部分(重点在于作者加粗的部分):

如果Trait Object被用作泛型类型的类型参数,则首先使用包含类型来尝试推断约束。

  • 如果包含类型有唯一的限制,则以该限制为默认值
  • 如果包含类型有多个限制,则必须指定显式限制

如果这些规则都不适用,则使用Trait的限制:

  • 如果Trait定义了单个生命周期限制,则使用该限制。
  • 如果任何生命周期限制使用'static,则使用'static。
  • 如果Trait没有生命周期限制,则在表达式中推断生命周期,并且在表达式之外为'static

哦,当然!我甚至没有考虑到特质上的生命周期,但现在明白了为什么编译器会发现生命周期不匹配。谢谢你! - NanoWizard

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