![](/img/trans.png)
[英]Why does creating a mutable reference to a dereferenced mutable reference work?
[英]Why does a mutable reference to a dropped object still count as a mutable reference?
这是一个简化的例子:
struct Connection {}
impl Connection {
fn transaction(&mut self) -> Transaction {
Transaction { conn: self }
}
}
struct Transaction<'conn> {
conn: &'conn Connection,
}
impl<'conn> Transaction<'conn> {
fn commit(mut self) {}
}
fn main() {
let mut db_conn = Connection {};
let mut trans = db_conn.transaction(); //1
let mut records_without_sync = 0_usize;
const MAX_RECORDS_WITHOUT_SYNC: usize = 100;
loop {
//do something
records_without_sync += 1;
if records_without_sync >= MAX_RECORDS_WITHOUT_SYNC {
trans.commit();
records_without_sync = 0;
trans = db_conn.transaction(); //2
}
}
}
编译器在1和2处报告两个可变借位,但事实并非如此。 由于trans.commit()
通过值获取self
,因此trans
被删除,因此第2点应该没有可变引用。
有一个可变的参考。
如果您将transaction
更改为:
fn transaction(&mut self) -> Transaction {
let _: () = self;
Transaction{conn: self}
}
你会看到编译器错误:
= note: expected type `()`
= note: found type `&mut Connection`
所以self
是类型&mut Connection
...一个可变的引用。 然后,您将此传递到从此函数返回的Transaction
实例中。
这意味着你的可变借用存在于trans
的生命周期中(由我添加的花括号来显示借用的范围):
let mut trans = db_conn.transaction();
{ // <-------------------- Borrow starts here
let mut records_without_sync = 0_usize;
const MAX_RECORDS_WITHOUT_SYNC: usize = 100;
loop {
//do something
records_without_sync += 1;
if records_without_sync >= MAX_RECORDS_WITHOUT_SYNC {
trans.commit();
records_without_sync = 0;
trans = db_conn.transaction();// <--- ####### D'oh! Still mutably borrowed
}
}
} // <-------------------- Borrow ends here
如果你正在寻找这种parent-><-child
设置,我认为你必须达到Rc<RefCell>
。
具体来说,引用的Rc
计算您传递连接的次数和RefCell
在运行时跟踪借用而不是编译时间。 是的,这确实意味着如果您设法在运行时尝试并可变地借两次,那么您会感到恐慌。 如果不了解您的架构,很难说这是否合适。
use std::cell::RefCell;
use std::rc::Rc;
struct Connection {}
impl Connection {
fn do_something_mutable(&mut self) {
println!("Did something mutable");
}
}
type Conn = Rc<RefCell<Connection>>;
struct Transaction {
conn: Conn,
}
impl Transaction {
fn new(connection: Conn) -> Transaction {
Transaction { conn: connection }
}
fn commit(mut self) {
self.conn.borrow_mut().do_something_mutable();
}
}
fn main() {
let db_conn = Rc::new(RefCell::new(Connection {}));
let mut trans = Transaction::new(db_conn.clone());
let mut records_without_sync = 0_usize;
const MAX_RECORDS_WITHOUT_SYNC: usize = 100;
loop {
//do something
records_without_sync += 1;
if records_without_sync >= MAX_RECORDS_WITHOUT_SYNC {
trans.commit();
records_without_sync = 0;
trans = Transaction::new(db_conn.clone());
break; // Used to stop the loop crashing the playground
}
}
}
您的原始代码在启用非词法生存期时有效 :
#![feature(nll)]
struct Connection {}
impl Connection {
fn transaction(&mut self) -> Transaction {
Transaction { conn: self }
}
}
struct Transaction<'conn> {
conn: &'conn Connection,
}
impl<'conn> Transaction<'conn> {
fn commit(self) {}
}
fn main() {
let mut db_conn = Connection {};
let mut trans = db_conn.transaction();
let mut records_without_sync = 0_usize;
const MAX_RECORDS_WITHOUT_SYNC: usize = 100;
loop {
//do something
records_without_sync += 1;
if records_without_sync >= MAX_RECORDS_WITHOUT_SYNC {
trans.commit();
records_without_sync = 0;
trans = db_conn.transaction();
}
}
}
非词汇生命周期提高了借阅检查器的精确度。 编译器变得更加智能,现在能够证明更多程序是内存安全的。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.