引用另一个字段中的向量元素的结构

struct with reference to element of a vector in another field

我有下面的例子,我想要一个结构,它在一个字段中保存一个数据向量,并且有另一个字段包含当前选定的字段。我的理解是,这在 rust 中是不可能的,因为我可以删除 selected 指向的 tables 中的元素,从而创建一个悬空指针(当存在不可变借用时不能借用 mut )。显而易见的解决方法是为元素存储 usize 索引,而不是 &'a String。但这意味着如果我从 tables 中删除一个元素,我需要更新索引。有什么方法可以使用智能指针来避免这种情况,或者一般来说只是更好的解决方案吗?我看过其他问题,但它们与下面的问题并不完全相同,并且有额外的信息让像我这样的初学者更难理解它们,而下面是一个非常简单的例子。

struct Data<'a> {
    selected: &'a String,
    tables: Vec<String>,
}

fn main() {
    let tables = vec!["table1".to_string(), "table2".to_string()];
    let my_stuff = Data {
        selected: &tables[0],
        tables: tables,
    };
}

您非常正确地评估了您编写它的方式是不可能的,因为 Rust 保证内存安全并将其存储为引用将有可能创建一个悬空指针。

我可以在这里看到几种解决方案。


静态字符串

这当然只有在存储 compile-time 静态字符串时才有效。

struct Data {
    selected: &'static str,
    tables: Vec<&'static str>,
}

fn main() {
    let tables = vec!["table1", "table2"];
    let my_stuff = Data {
        selected: &tables[0],
        tables,
    };
}

之所以有效,是因为静态字符串 non-mutable 并且保证永远不会被释放。此外,如果这令人困惑,我建议阅读 Strings and str slices.

之间的区别

您甚至可以更进一步,将生命周期缩短至 'a。但是,您必须将它们作为 &'a str 存储在向量中,以确保它们不能被编辑。 但这允许您在其中存储 Strings,只要字符串可以在 Data 对象的整个生命周期内借用。

struct Data<'a> {
    selected: &'a str,
    tables: Vec<&'a str>,
}

fn main() {
    let str1 = "table1".to_string();
    let str2 = "table2".to_string();
    let tables = vec![str1.as_str(), str2.as_str()];

    let my_stuff = Data {
        selected: &tables[0],
        tables,
    };
}

引用计数智能指针

根据您的情况,推荐几种类型:

  • Rc<...> - 如果你的数据是不可变的。否则,您需要创建 interior mutability with:
  • Rc<Cell<...>> - 如果您的问题是 single-threaded 并且处理的是简单数据类型
  • ,那么最安全和最好的解决方案
  • Rc<RefCell<...>> - 对于必须更新的更复杂的数据类型 in-place 并且不能只是移入和移出
  • Arc<Mutex<...>> - 一旦您的问题涉及多个线程

在你的情况下,数据实际上很简单,你的程序是 single-threaded,所以我会选择:

use std::{cell::Cell, rc::Rc};

struct Data {
    selected: Rc<Cell<String>>,
    tables: Vec<Rc<Cell<String>>>,
}

fn main() {
    let tables = vec![
        Rc::new(Cell::new("table1".to_string())),
        Rc::new(Cell::new("table2".to_string())),
    ];
    let my_stuff = Data {
        selected: tables[0].clone(),
        tables,
    };
}

当然,如果你不想在创建后修改你的字符串,你可以选择:

use std::rc::Rc;

struct Data {
    selected: Rc<String>,
    tables: Vec<Rc<String>>,
}

fn main() {
    let tables = vec![Rc::new("table1".to_string()), Rc::new("table2".to_string())];
    let my_stuff = Data {
        selected: tables[0].clone(),
        tables,
    };
}

隐藏数据结构并使用索引

正如您已经提到的,您可以改用索引。然后你必须隐藏向量并提供 getters/setters/modifiers 以确保索引在向量更改时保持同步。

我会把实现保持在 reader 并且不会在这里提供示例:)


我希望这已经对您有所帮助,或者至少给了您一些新想法。我很高兴看到新人来到社区,所以如果您有任何问题,请随时提出进一步的问题:)