我在某个地方读到过,在一种以指针为特征的语言中,编译器不可能在编译时出于各种原因完全决定所有指针是否正确使用和/或有效(指活动对象),因为这基本上构成了解决暂停问题.直觉上,这并不奇怪,因为在这种情况下,我们可以推断程序在编译时的运行时行为,类似于this related question中所述.

然而,据我所知,Rust语言要求指针判断完全在编译时完成(没有与指针相关的未定义行为,至少没有"安全"指针,也没有"无效指针"或"空指针"运行时异常).

假设Rust编译器不能解决暂停问题,那么谬论在哪里呢?

  • 与C中的原始指针相比,pointer checking isn't done entirely at compile-time,和Rust的智能指针是否仍然会带来一些运行时开销?
  • 或者,Rust编译器可能无法做出完全正确的决定,有时需要信任程序员吗™, 可能是使用了一个终生注释(<'lifetime_ident>语法的注释)?在这种情况下,这是否意味着指针/内存安全保证不是<'lifetime_ident>%,仍然依赖于程序员编写正确的代码?
  • 另一种可能是,Road指针在某种意义上是非"通用"的,或者是受限的,这样编译器就可以在编译时完全推断它们的属性,但是它们并不像C++中的C或智能指针中的原始指针那么有用.
  • Or maybe it is something completely different and I'm misinterpreting one or more of
    { "pointer", "safety", "guaranteed", "compile-time" }.

推荐答案

Disclaimer:我有点赶时间,所以这有点迂回.请随意清理.

The One Sneaky Trick That Language Designers Hate™ is basically this: Rust can only reason about the 'static lifetime (used for global variables and other whole-program lifetime things) and the lifetime of stack (i.e. local) variables: it cannot express or reason about the lifetime of heap allocations.

这意味着一些事情.首先,所有处理堆分配的库类型(i.e.Box<T>Rc<T>Arc<T>)都是它们所指向的.因此,它们实际上并不是为了生存而存在的.

当你访问智能指针的contents时,你需要do个生命周期.例如:

let mut x: Box<i32> = box 0;
*x = 42;

在第二行的幕后发生的是:

{
    let box_ref: &mut Box<i32> = &mut x;
    let heap_ref: &mut i32 = box_ref.deref_mut();
    *heap_ref = 42;
}

换句话说,因为Box不是魔法,我们必须告诉编译器如何将其转换为常规的、普通的指针.这就是DerefDerefMuttrait 的用途.这就提出了一个问题:heap_ref的生命周期 到底是多少?

答案在DerefMut的定义中(因为我赶时间,所以记忆中有DerefMut):

trait DerefMut {
    type Target;
    fn deref_mut<'a>(&'a mut self) -> &'a mut Target;
}

就像我之前说的,我们谈论的是"堆生命周期 ".相反,它必须将分配给i32的堆的生存期绑定到它手头上唯一的另一个生存期:Box的生存期.

这意味着"复杂"的事物没有一个可表达的生命周期,因此它们必须完成它们管理的事物.当你把一个复杂的智能指针/句柄转换成一个简单的borrow 指针时,that是你必须引入一个生命周期的时刻,你通常只使用句柄本身的生命周期.

实际上,我应该澄清一下:"句柄的生存期",我真正的意思是"当前存储句柄的变量的生存期":生存期实际上是storage,而不是values.这就是为什么Rust 的新来者在不明白为什么他们不能做以下事情时会被绊倒的典型原因:

fn thingy<'a>() -> (Box<i32>, &'a i32) {
    let x = box 1701;
    (x, &x)
}

"But... I know that the box will continue to live on, why does the compiler say it doesn't?!"因为Rust无法解释堆的生命周期,must求助于将&x的生命周期与它碰巧指向的堆分配的variable xnot联系起来.

Rust相关问答推荐

为什么是!为Rust中的RwLockReadGuard和RwLockWriteGuard实现的发送特征?

trait声明中的生命周期参数

从特征实现调用函数的Rust惯用方法

是否有可能同时避免不兼容的不透明类型和代码重复?

有没有更好的方法从HashMap的条目初始化 struct ?

通过解引用将值移出Box(以及它被脱糖到什么地方)?

在铁 rust 中传递所有权

变量需要parse()中的显式类型

如何初始化选项<;T>;数组Rust 了?

了解Rust';s特征对象和不同函数签名中的生存期注释

在 Rust 中用问号传播错误时对类型转换的困惑?

为什么特征默认没有调整大小?

将 &str 或 String 保存在变量中

在 Rust 中,为什么整数溢出有时会导致编译错误或运行时错误?

`use std::error::Error` 声明中断编译

如何存储返回 Future 的闭包列表并在 Rust 中的线程之间共享它?

当用作函数参数时,不强制执行与绑定的关联类型

如果我立即等待,为什么 `tokio::spawn` 需要一个 `'static` 生命周期?

在 Rust 中组合特征的不同方法是否等效?

您不能borrow 对只读值的可变引用