fn main() {
let mut v = vec![];
v.push(1);
let v1 = &v[0];
v.push(2);
println!("{}", v1);
}
这种c++很经典的内存安全错误,始终没法理解编译器是如何检测的 v1的类型是&i32,为什么v1的借用会影响了v的借用
1
共 7 条评论, 1 页
fn main() {
let mut v = vec![];
v.push(1);
let v1 = &v[0];
v.push(2);
println!("{}", v1);
}
这种c++很经典的内存安全错误,始终没法理解编译器是如何检测的 v1的类型是&i32,为什么v1的借用会影响了v的借用
评论区
写评论受教了👍
👇
苦瓜小仔: > 为什么v1的借用会影响了v的借用
首先要知道你的代码中,引用的生命周期是什么样的(不展开方法调用、语法脱糖、宏展开等细节内容)
引用是一种 primitive 类型,并且:
Index
trait 的函数/方法fn index(&self, index: Idx) -> &Self::Output
约定返回的引用必须与&self
活得一样长v1
的类型为&'2 i32
,从地点 2 存活到地点 4,所以根据函数契约,那个&'2 v
在 2、3、4 三处存活。但在地点 3 有两种引用存活:
&'3 mut v
和&'2 v
,这违反了引用的 两大规则 之一(任何一个给定的时间/地点,要么只有一个&mut T
存活,要么全部是&T
存活)。当前借用检查的实现被称为 NLL,所以 RFC 2094 是必看的,因为它是 NLL 的设计文稿。尤其从 RFC 2094 (NLL): 什么是生命周期?它如何与借用检查器交互? 开始。
概括地说,编译器会将 Rust 源码脱糖到 MIR (除去所有语法糖的类 Rust 代码,比如上面我列的所有方法调用变成纯函数调用),然后追踪每个生命周期(生命周期视为控制流图中的一个点集)和约束,最终处理这些约束。
MIRI (MIR 解释器)使用 stack borrows(和新一代的 tree borrows)算法对当前借用检查进行建模,所以它也可以帮助你理解 Rust 的借用检查。
大神👍 ,讲得浅显易懂,学习了,感谢分享!👌
--
👇
苦瓜小仔: > 为什么v1的借用会影响了v的借用
首先要知道你的代码中,引用的生命周期是什么样的(不展开方法调用、语法脱糖、宏展开等细节内容)
引用是一种 primitive 类型,并且:
Index
trait 的函数/方法fn index(&self, index: Idx) -> &Self::Output
约定返回的引用必须与&self
活得一样长v1
的类型为&'2 i32
,从地点 2 存活到地点 4,所以根据函数契约,那个&'2 v
在 2、3、4 三处存活。但在地点 3 有两种引用存活:
&'3 mut v
和&'2 v
,这违反了引用的 两大规则 之一(任何一个给定的时间/地点,要么只有一个&mut T
存活,要么全部是&T
存活)。当前借用检查的实现被称为 NLL,所以 RFC 2094 是必看的,因为它是 NLL 的设计文稿。尤其从 RFC 2094 (NLL): 什么是生命周期?它如何与借用检查器交互? 开始。
概括地说,编译器会将 Rust 源码脱糖到 MIR (除去所有语法糖的类 Rust 代码,比如上面我列的所有方法调用变成纯函数调用),然后追踪每个生命周期(生命周期视为控制流图中的一个点集)和约束,最终处理这些约束。
MIRI (MIR 解释器)使用 stack borrows(和新一代的 tree borrows)算法对当前借用检查进行建模,所以它也可以帮助你理解 Rust 的借用检查。
回答太棒了,深受启发,学习了~~
首先要知道你的代码中,引用的生命周期是什么样的(不展开方法调用、语法脱糖、宏展开等细节内容)
引用是一种 primitive 类型,并且:
Index
trait 的函数/方法fn index(&self, index: Idx) -> &Self::Output
约定返回的引用必须与&self
活得一样长v1
的类型为&'2 i32
,从地点 2 存活到地点 4,所以根据函数契约,那个&'2 v
在 2、3、4 三处存活。但在地点 3 有两种引用存活:
&'3 mut v
和&'2 v
,这违反了引用的 两大规则 之一(任何一个给定的时间/地点,要么只有一个&mut T
存活,要么全部是&T
存活)。当前借用检查的实现被称为 NLL,所以 RFC 2094 是必看的,因为它是 NLL 的设计文稿。尤其从 RFC 2094 (NLL): 什么是生命周期?它如何与借用检查器交互? 开始。
概括地说,编译器会将 Rust 源码脱糖到 MIR (除去所有语法糖的类 Rust 代码,比如上面我列的所有方法调用变成纯函数调用),然后追踪每个生命周期(生命周期视为控制流图中的一个点集)和约束,最终处理这些约束。
MIRI (MIR 解释器)使用 stack borrows(和新一代的 tree borrows)算法对当前借用检查进行建模,所以它也可以帮助你理解 Rust 的借用检查。
--
👇
ianfor: 额 我的意思是v1类型是&i32, 他是如何让v不能在借用的 编译器是怎么实现的
为什么v1会导致v的借用,不是很理解这个
--
👇
ribs: i32 是编译器推断出来的,没有任何地方表明他是其他类型,那他默认就是 i32
push 接受的是可变引用&mut self,可变引用不能跟不可变引用共存,这是借用规则定的
额 我的意思是v1类型是&i32, 他是如何让v不能在借用的 编译器是怎么实现的
--
👇
ribs: i32 是编译器推断出来的,没有任何地方表明他是其他类型,那他默认就是 i32
push 接受的是可变引用&mut self,可变引用不能跟不可变引用共存,这是借用规则定的
i32 是编译器推断出来的,没有任何地方表明他是其他类型,那他默认就是 i32
push 接受的是可变引用&mut self,可变引用不能跟不可变引用共存,这是借用规则定的