Rust 为什么对已删除对象的可变引用仍然算作可变引用?
以下是一个简化的示例:Rust 为什么对已删除对象的可变引用仍然算作可变引用?,rust,lifetime,Rust,Lifetime,以下是一个简化的示例: struct Connection {} impl Connection { fn transaction(&mut self) -> Transaction { Transaction { conn: self } } } struct Transaction<'conn> { conn: &'conn Connection, } impl<'conn> Transaction&
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
}
}
}
struct连接{}
impl连接{
fn事务(&mut self)->事务{
事务{conn:self}
}
}
结构事务{
fn提交(多自){}
}
fn main(){
设mut db_conn=Connection{};
让mut trans=db_conn.transaction();//1
让mut记录而不同步=0;
const MAX_RECORDS_而不同步:usize=100;
环路{
//做点什么
不带同步的记录数+=1;
如果记录不同步>=最大记录不同步{
trans.commit();
没有同步的记录=0;
trans=db_conn.transaction();//2
}
}
}
编译器在1和2报告两个可变借用,但事实并非如此。由于trans.commit()
按值获取self
,因此trans
被删除,因此按点2应该没有可变引用
有一个可变的引用 如果将
事务
更改为:
fn transaction(&mut self) -> Transaction {
let _: () = self;
Transaction{conn: self}
}
您将看到编译器出现以下错误:
= note: expected type `()`
= note: found type `&mut Connection`
因此,self
属于&mut连接类型
。。。可变引用。然后将其传递到从该函数返回的事务
实例中
这意味着您的可变借用在trans
的生命周期内存在(我添加了花括号以显示借用的范围):
启用以下选项时,原始代码可以正常工作:
#![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();
}
}
}
#![专题(nll)]
结构连接{}
impl连接{
fn事务(&mut self)->事务{
事务{conn:self}
}
}
结构事务{
fn提交(自){}
}
fn main(){
设mut db_conn=Connection{};
让mut trans=db_conn.transaction();
让mut记录而不同步=0;
const MAX_RECORDS_而不同步:usize=100;
环路{
//做点什么
不带同步的记录数+=1;
如果记录不同步>=最大记录不同步{
trans.commit();
没有同步的记录=0;
trans=db_conn.transaction();
}
}
}
非词汇生存期提高了借用检查器的精度。编译器变得更加智能,现在能够证明更多的程序是内存安全的。你不明白我的意思。在
trans.commit()之后代码>trans不存在。所以不存在可变引用。您的解决方案添加了额外的内存分配,以及额外的计数器。如果没有这些不必要的东西,有可能达到同样的目标吗?谢谢你的回答。@user1244932“所以没有可变的引用”-当然,在运行时。尽管我知道在编译时,借用检查器无法静态地判断这一点。“如果没有这些不必要的东西,有可能实现同样的目标吗?”——我相信是的,但这需要重新构造代码,使其不具有父级->@user1244932:有希望,在某个时候,Rust将获得非词汇借用;也就是说,不是通过词法范围(从变量引入到结束}
)跟踪借词,而是根据变量的生存期跟踪借词。不过,我认为现在还没有人在研究它。最终,锈还没有出现。
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();
}
}
}