引用另一个字段中的向量元素的结构
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
存储在向量中,以确保它们不能被编辑。
但这允许您在其中存储 String
s,只要字符串可以在 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 并且不会在这里提供示例:)
我希望这已经对您有所帮助,或者至少给了您一些新想法。我很高兴看到新人来到社区,所以如果您有任何问题,请随时提出进一步的问题:)
我有下面的例子,我想要一个结构,它在一个字段中保存一个数据向量,并且有另一个字段包含当前选定的字段。我的理解是,这在 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
存储在向量中,以确保它们不能被编辑。
但这允许您在其中存储 String
s,只要字符串可以在 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 并且不会在这里提供示例:)
我希望这已经对您有所帮助,或者至少给了您一些新想法。我很高兴看到新人来到社区,所以如果您有任何问题,请随时提出进一步的问题:)