在C++中,具有如下排列是非常标准的:
// Some object that you want to manipulate
class DataType
{
public:
foo() {}
bar() {}
};
// Some class that manipulates a DataType on construct and destruct
// Your typical RAII scope side effect type thing
class DoFooAndBar
{
public:
DoFooAndBar(DataType& dataType) : m_dataType(dataType)
{ m_dataType.foo(); }
~DoFooAndBar()
{ m_dataType.bar(); }
private:
DataType& m_dataType
}
我试图在Rust中做类似的事情,但我遇到了DoFooAndBar采用可变引用的问题,随后的代码无法采用不可变的引用.我可以用你典型的Rc::RefCell
黑客来处理这个问题,但我想知道是否有一种正确的方法来做这样的事情而不诉诸那个.
以下是我的铁 rust 代码,我尽可能地为这个问题做了一些修改:
/// Symbol table example to for a hypothetical script interpreter
/// Simplified to demonstrate issues with borrowing references
use std::collections::HashMap;
/// For our purposes a frame from is just a string->string map
type SymbolFrame = HashMap<String, String>;
/// Our stack of frames is just a vector of stack frame
/// Not performant, but that's not the point
type SymbolFrameStack = Vec<SymbolFrame>;
/// The SymbolTable type contains a stack of symbol frames
/// and routines for working with them
pub struct SymbolTable
{
frames: SymbolFrameStack
}
impl SymbolTable
{
/// Start with an initial stack with one frame
pub fn new() -> Self { SymbolTable { frames: vec![SymbolFrame::new()] } }
/// Push and pop frames
pub fn push(&mut self) { self.frames.push(SymbolFrame::new()); }
pub fn pop(&mut self) { self.frames.pop(); }
/// See if a symbol exists by name anywhere in the frames
pub fn contains(&self, name: &str) -> bool {
for frame in self.frames.iter() {
if frame.contains_key(name) {
return true;
}
}
return false;
}
}
/// Push a frame on new(); pop the frame on drop()
pub struct SymbolStacker<'a> { table: &'a mut SymbolTable }
impl<'a> SymbolStacker<'a>
{
pub fn new(table: &'a mut SymbolTable) -> SymbolStacker {
table.push();
SymbolStacker { table }
}
}
impl<'a> Drop for SymbolStacker<'a> {
fn drop(&mut self) {
self.table.pop();
}
}
#[test]
fn bad_test_symbol_table() {
// Create our table for testing
let table = &mut SymbolTable::new();
{
// Enter a new scope of code, pushing a new stack frame
let _stacker1 = SymbolStacker::new(table);
{
// ...a lot of other recursive code
// ...presumably passing the table struct around in some way
// ...we just try to see if a symbol exists in the table
assert!(!table.contains("foo"));
}
assert!(table.contains("foo"));
}
}
它在运行测试时生成编译错误:
error[E0502]: cannot borrow `*table` as immutable because it is also borrowed as mutable
我理解这个错误,我只是在寻求关于如何最好地在Rust中做这类事情的建议.