rust - 为什么对已删除对象的可变引用仍算作可变引用?
Posted 跨链技术践行者
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了rust - 为什么对已删除对象的可变引用仍算作可变引用?相关的知识,希望对你有一定的参考价值。
这是一个简化的示例:
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点点,不应有任何可变引用。
- 为什么编译器看不到 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
会在运行时(而不是在编译时)跟踪借入。是的,这的确意味着如果您尝试在运行时两次尝试并可变地借用它,就会感到 panic 。在不了解您的体系结构的情况下,很难说这是否合适。Here is my solution anyway:
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
}
}
}
以上是关于rust - 为什么对已删除对象的可变引用仍算作可变引用?的主要内容,如果未能解决你的问题,请参考以下文章
为啥在 Rust 中将 const 引用直接转换为可变引用无效?