我有Vec<MyType>英镑.对于我的算法,我需要根据不同的标准对MyType个元素进行排序.一个是插入顺序,直接使用Vec<MyType>:我只是索引向量中的元素.

我还需要按照其他两个标准对MyType个元素进行排序.

在C++中,我只需创建一个std::set<size_t>,在Vec<MyType>中插入元素的索引,并提供一个带有指向向量的指针/引用的比较器对象,这样我就可以从索引中获取元素,并以我喜欢的方式进行比较.

根据this answer,我不能用Rust的BTreeSet做到这一点,我必须实现trait Ord.但我无法实现Ord,除非我还在BTreeSet的每个元素中存储对Vec<MyType>的引用.我不想复制元素,因为它们是大而复杂的对象.

我也try 过使用Vec<Box<MyType>>BTreeSet<&MyType>,但后来我发现我不能在同一个 struct 中同时使用这两个,因为 struct 不能使用自己的生存期存储引用.

那么,有没有一种铁 rust 惯用且安全的方法来做到这一点(最好不用Rc<>,因为我知道我的元素永远不会被移除)?

struct MyType {
    /// Ordered by insertion order.
    basis: Vec<Box /* ?? maybe ?? */<MyType>>;

    /// Alternative ordering for basis.
    alt1_ordering: BTreeSet</*???*/>;

    /// Other alternative ordering for basis.
    alt2_orderign: BTreeSet</*???*/>;
}

推荐答案

那么,有没有一种可靠的方法可以做到这一点

最好不用Rc<>

哦嗯,没有.但是Rc的开销应该可以忽略不计,这使得实现非常简单.您已经需要一个智能指针类型,因为该 struct 是递归的.

// Newtypes for implementing your alt1 and alt2 ordering.
struct Alt1Ordering(Rc<MyType>);
impl PartialEq for Alt1Ordering { ... }
impl PartialOrd for Alt1Ordering { ... }
impl Eq for Alt1Ordering {}
impl Ord for Alt1Ordering { ... }

struct Alt2Ordering(Rc<MyType>);
impl PartialEq for Alt2Ordering { ... }
impl PartialOrd for Alt2Ordering { ... }
impl Eq for Alt2Ordering {}
impl Ord for Alt2Ordering { ... }

struct MyType {
    /// Ordered by insertion order.
    basis: Vec<Rc<MyType>>,

    /// Alternative ordering for basis.
    alt1_ordering: BTreeSet<Alt1Ordering>,

    /// Other alternative ordering for basis.
    alt2_ordering: BTreeSet<Alt2Ordering>,
}

impl MyType {
    pub fn push(&mut self, value: MyType) {
        let value = Rc::new(value);
        
        self.basis.push(value.clone());
        self.alt1_ordering.insert(Alt1Ordering(value.clone()));
        self.alt2_ordering.insert(Alt2Ordering(value));
    }

    // Whatever accessors you need, e.g.:
    pub fn iter_basis(&self) -> impl Iterator<Item=&MyType> {
        self.basis.iter().map(|v| &**v)
    }
    
    pub fn iter_alt1(&self) -> impl Iterator<Item=&MyType> {
        self.alt1_ordering.iter().map(|v| &*v.0)
    }
    
    pub fn iter_alt2(&self) -> impl Iterator<Item=&MyType> {
        self.alt2_ordering.iter().map(|v| &*v.0)
    }
}

请注意,MyType既不是Send也不是Sync,因为它包含Rc.然而,只要clone或销毁Rc的所有方法都取&mut self,那么实现SendSync应该是安全的,因为如果有&mut MyType,那么就不应该存在其他引用.

请注意,您没有编写的代码可能会使SendSync实现不健全.例如,如果您#[derive(Clone)],编译器生成的实现将使trait实现不健全,因为它将使用共享引用克隆Rc.

或者,您可以将Rc替换为Arc,以安全地获得SendSync,而无需考虑克隆/销毁Rc的位置.


从理论上讲,不使用钉扎Rc就可以做到这一点,但这需要unsafe,只有当Rc的开销被证明很重要时,我才会研究这条路径(我怀疑会是这样).

Rust相关问答推荐

如何从接收&;self的方法克隆RC

有没有办法模仿对象安全克隆?

如何在Tauri中将变量从后端传递到前端

如何实现泛型枚举的`Serde::Desialize`特性

如何在Rust中将选项<;选项<;字符串>;转换为选项<;选项&;str>;?

为什么基于高山Linux的Docker镜像不能在绝对路径下找到要执行的命令?

如何创建一个可变的嵌套迭代器?

如何实现Deref;多次;?

在什么情况下 `..._or()` 比 `..._or_else(|| {})` 更好,为什么?

.在 Rust 模块标识符中

结果流到 Vec 的结果:如何避免多个into_iter和collect?

枚举的利基优化如何在 Rust 中工作?

从Rust 的临时文件中创建引用是什么意思?

如何在 Rust 中将枚举变体转换为 u8?

Rust 生命周期:这两种类型声明为不同的生命周期

将一片字节复制到一个大小不匹配的数组中

如何在 Rust 中创建最后一个元素是可变长度数组的 struct ?

如何存储返回 Future 的闭包列表并在 Rust 中的线程之间共享它?

如何在 nom 中构建负前瞻解析器?

为什么这里需要类型注解?