The*_*ant 15 pointers rust memory-safety
我已经读过某个地方,在一个具有指针的语言中,编译器无法在编译时完全决定是否所有指针都被正确使用和/或有效(参考活动对象)由于各种原因,因为那样做基本上构成解决停止问题.直觉上这并不奇怪,因为在这种情况下,我们能够在编译期间推断出程序的运行时行为,类似于此相关问题中所述的内容.
但是,据我所知,Rust语言要求指针检查完全在编译时完成(没有与指针相关的未定义行为,至少是"安全"指针,并且没有"无效指针"或"空指针"运行时例外).
假设Rust编译器无法解决暂停问题,那么谬误在哪里呢?
<'lifetime_ident>
语法的注释)?在这种情况下,这是否意味着指针/内存安全保证不是100%,仍然依赖程序员编写正确的代码?{ "pointer", "safety", "guaranteed", "compile-time" }
.免责声明:我有点匆忙,所以这有点蜿蜒.随意清理它.
语言设计师讨厌的一个狡猾的伎俩基本上是这样的:Rust 只能推断'static
生命周期(用于全局变量和其他整个程序的生命周期事物)和堆栈(即本地)变量的生命周期:它无法表达或推理堆分配的生命周期.
这意味着一些事情.首先,所有以堆分配处理的库类型(即 Box<T>
,Rc<T>
,Arc<T>
),都拥有它们指向的东西.结果,他们实际上并不需要生命来存在.
当你做需要的寿命是当你所访问的内容,一个智能指针.例如:
let mut x: Box<i32> = box 0;
*x = 42;
Run Code Online (Sandbox Code Playgroud)
在第二行的幕后发生的是这样的:
{
let box_ref: &mut Box<i32> = &mut x;
let heap_ref: &mut i32 = box_ref.deref_mut();
*heap_ref = 42;
}
Run Code Online (Sandbox Code Playgroud)
换句话说,因为Box
它不是魔术,我们必须告诉编译器如何将它转换成磨机借用指针的常规运行.这就是特征Deref
和DerefMut
特征.这提出了一个问题:究竟是什么样的生命周期heap_ref
?
对此的答案是DerefMut
(从记忆中因为我匆忙)的定义:
trait DerefMut {
type Target;
fn deref_mut<'a>(&'a mut self) -> &'a mut Target;
}
Run Code Online (Sandbox Code Playgroud)
就像我之前说的那样,Rust 绝对不能谈论"堆生命周期".相反,它必须将分配的堆的i32
生命周期与它手头的唯一其他生命周期联系起来:生命周期Box
.
这意味着"复杂"的东西没有明确的生命周期,因此必须拥有他们管理的东西.当您将一个复杂的智能指针/处理成简单借来的指针,那就是你必须引入一生的那一刻,你通常只使用手柄本身的寿命.
实际上,我应该澄清:通过"句柄的生命周期",我的意思是"当前存储句柄的变量的生命周期":生命周期实际上是存储,而不是值.这通常是为什么Rust的新人在他们无法解决为什么他们不能做以下事情时会绊倒的原因:
fn thingy<'a>() -> (Box<i32>, &'a i32) {
let x = box 1701;
(x, &x)
}
Run Code Online (Sandbox Code Playgroud)
"但是......我知道盒子会继续存在,为什么编译器说它没有?!" 因为Rust无法推断堆生命周期,并且必须求助于将生命周期&x
与变量联系起来 x
,而不是它恰好指向的堆分配.
是不是指针检查不是完全在编译时完成的,而Rust的智能指针仍然会引入一些运行时开销,比如C中的原始指针?
对编译时无法检查的内容进行特殊的运行时检查.这些通常在cell
箱子里找到.但是一般来说,Rust会在编译时检查所有内容,并且应该生成与C中相同的代码(如果你的C代码没有做未定义的东西).
或者Rust编译器可能无法做出完全正确的决策,有时需要Just Trust The Programmer™,可能使用其中一个生命周期注释(具有<'lifetime_ident>语法的注释)?在这种情况下,这是否意味着指针/内存安全保证不是100%,仍然依赖程序员编写正确的代码?
如果编译器无法做出正确的决定,则会出现编译时错误,告诉您编译器无法验证您正在执行的操作.这也可能会限制你知道正确的东西,但编译器却没有.unsafe
在这种情况下,您总是可以转到代码.但正如您所正确的那样,编译器部分依赖于程序员.
编译器会检查函数的实现,看它是否完全符合生命周期的说法.然后,在函数的调用站点,它检查程序员是否正确使用该函数.这类似于类型检查.C++编译器会检查您是否返回了正确类型的对象.然后,如果返回的对象存储在正确类型的变量中,它将在调用站点进行检查.函数的程序员决不会破坏承诺(除非unsafe
使用了,但是你总是可以让编译器强制执行unsafe
你的项目中没有使用)
Rust不断改进.一旦编译器变得更聪明,Rust中的更多东西可能合法.
另一种可能性是Rust指针在某种意义上是非"通用的"或受限制的,因此编译器可以在编译时完全推断它们的属性,但它们不像C中的原始指针或C++中的智能指针那样有用.
在C中有一些可能出错的事情:
这些不会发生在安全的Rust中.
Box
分配对象(类似但不等于unique_ptr
C++中的a)Box
es自动释放内存.在C++中有一些可能出错的地方:
free
.您仍然可以创建悬空参考:auto x = make_unique<int>(42);
auto& y = *x;
x.reset();
y = 99;
Rust修复了那些:
y
存在,您可能无法修改x
.这在编译时进行检查,不能被更多级别的间接或结构所规避.我已经读过某个地方,在一个具有指针的语言中,编译器无法在编译时完全决定是否所有指针都被正确使用和/或有效(参考活动对象)由于各种原因,因为那样做基本上构成解决停止问题.
Rust并不能证明你所有的指针都使用得当.你可能仍然在写虚假程序.Rust证明您没有使用无效指针.Rust证明你永远不会有空指针.Rust证明你永远不会有两个指向同一个对象的指针,除非所有这些指针都是不可变的(const).Rust不允许你编写任何程序(因为那将包括违反内存安全的程序).现在Rust仍然阻止你编写一些有用的程序,但是有计划允许更多(合法)程序用安全的Rust编写.
直觉上这并不奇怪,因为在这种情况下,我们能够在编译期间推断出程序的运行时行为,类似于此相关问题中所述的内容.
重新讨论关于暂停问题的引用问题中的示例:
void foo() {
if (bar() == 0) this->a = 1;
}
Run Code Online (Sandbox Code Playgroud)
上面的C++代码看起来像Rust中的两种方式之一:
fn foo(&mut self) {
if self.bar() == 0 {
self.a = 1;
}
}
fn foo(&mut self) {
if bar() == 0 {
self.a = 1;
}
}
Run Code Online (Sandbox Code Playgroud)
对于任意的bar
你无法证明这一点,因为它可能会访问全局状态.Rust很快就会获得const
函数,这些函数可用于在编译时计算内容(类似于constexpr
).如果bar
是const
,那么证明在编译时self.a
是否设置变得微不足道1
.除此之外,如果没有pure
函数内容的功能或其他限制,您永远无法证明是否self.a
设置1
.
Rust目前不关心您的代码是否被调用.它关心self.a
在分配期间是否仍然存在内存.self.bar()
永远不会破坏self
(unsafe
代码除外).因此self.a
,if
分支机构内部始终可用.
归档时间: |
|
查看次数: |
1948 次 |
最近记录: |