【发布时间】:2020-12-04 02:21:42
【问题描述】:
我正在实现一些递归代码,其中调用堆栈更深处的函数实例可能需要引用来自先前帧的数据。但是,我只能对这些数据进行非 mut 访问,因此我将这些数据作为参考接收。因此,我需要将这些数据的引用保存在可以从更深的实例访问的堆栈数据结构中。
举例说明:
// I would like to implement this RefStack class properly, without per-item memory allocations
struct RefStack<T: ?Sized> {
content: Vec<&T>,
}
impl<T: ?Sized> RefStack<T> {
fn new() -> Self { Self{ content: Vec::new() } }
fn get(&self, index: usize) -> &T { self.content[index] }
fn len(&self) -> usize { self.content.len() }
fn with_element<F: FnOnce(&mut Self)>(&mut self, el: &T, f: F) {
self.content.push(el);
f(self);
self.content.pop();
}
}
// This is just an example demonstrating how I would need to use the RefStack class
fn do_recursion(n: usize, node: &LinkedListNode, st: &mut RefStack<str>) {
// get references to one or more items in the stack
// the references should be allowed to live until the end of this function, but shouldn't prevent me from calling with_element() later
let tmp: &str = st.get(rng.gen_range(0, st.len()));
// do stuff with those references (println is just an example)
println!("Item: {}", tmp);
// recurse deeper if necessary
if n > 0 {
let (head, tail): (_, &LinkedListNode) = node.get_parts();
manager.get_str(head, |s: &str| // the actual string is a local variable somewhere in the implementation details of get_str()
st.with_element(s, |st| do_recursion(n - 1, tail, st))
);
}
// do more stuff with those references (println is just an example)
println!("Item: {}", tmp);
}
fn main() {
do_recursion(100, list /* gotten from somewhere else */, &mut RefStack::new());
}
在上面的示例中,我关心的是如何在没有任何每个项目内存分配的情况下实现RefStack。 Vec 的偶尔分配是可以接受的——这些分配很少而且介于两者之间。 LinkedListNode 只是一个例子——实际上它是一些复杂的图形数据结构,但同样适用——我只有一个非 mut 引用,而给 manager.get_str() 的闭包只提供了一个非 mut @ 987654326@。请注意,传入闭包的非mut str 只能在get_str() 实现中构造,因此我们不能假设所有&str 具有相同的生命周期。
如果不将str 复制到拥有的Strings 中,我相当肯定RefStack 不能在安全的Rust 中实现,所以我的问题是如何在不安全的Rust 中实现这一点。感觉我可能能够得到这样的解决方案:
- 不安全仅限于
RefStack的执行 -
st.get()返回的引用应该至少与do_recursion函数的当前实例一样长(特别是,它应该能够在对st.with_element()的调用之后存活,这在逻辑上是安全的,因为st.get()返回的&T并不是指RefStack拥有的任何内存)
如何在(不安全的)Rust 中实现这样的结构?
感觉我可以将元素引用转换为指针并将它们存储为指针,但是在将它们转换回引用时,我仍然会遇到表达上述第二个要点中的要求的困难。还是有更好的方法(或者这样的结构可以在安全的 Rust 中实现,或者已经在某个库中)?
【问题讨论】:
-
通过避免引用的不同方法可能会更好地解决您的问题,但很难说,因为您没有描述您要解决的实际问题。也就是说,我认为这本身仍然是一个很好的问题,即使它不是解决您问题的最佳方法。
-
您需要随机访问堆栈元素,还是只需要迭代访问?
-
@MatthieuM。我需要随机访问堆栈元素。我需要的元素的索引通常取决于从当前
LinkedListNode的head计算的一些属性。 -
@SvenMarnach 我认为那里仍然存在一些不安全因素 - 第 31 行的变量
tmp可能会比它最初插入的帧的寿命更长。 -
@Bernard 我不这么认为,因为
get()方法返回的引用具有传入的&self引用的生命周期,而不是生命周期'a。
标签: recursion rust stack lifetime unsafe