我的印象是,Rust 旨在用于高度安全的系统。然后我注意到原始指针允许任意指针算术,它们会导致内存安全和安全问题。
1 回答
基本上,指针是指向另一个对象的对象。在大多数编程语言中(我猜),指针实际上只是一个引用内存地址的数字。Rust 的原始指针实际上就是这样 - 内存地址。Rust 中还有其他指针类型(&
引用Box
、、、、 ) Rc
,Arc
编译器可以针对这些类型验证内存是否有效并包含程序认为它包含的内容。原始指针不是这种情况;它们原则上可以指向任何内存位置,而不管内容如何。有关详细信息,请参阅本书。
原始指针只能在unsafe
块内取消引用。这些块是程序员告诉编译器“我比你更清楚这是安全的,我保证不会做任何愚蠢的事情”的一种方式。
如果可能,通常最好避免使用原始指针,因为编译器无法推断它们的有效性,这使得它们通常不安全。使原始指针不安全的事情有可能...
- 访问 NULL 指针,
- 访问一个悬空的(释放的或无效的)指针,
- 多次释放指针,
所有这些都归结为取消引用指针。也就是说,要使用指向的内存。
但是,使用原始指针而不取消引用它们是非常安全的。这有一个用例来确定两个引用是否指向同一个对象:
fn is_same(a: &i32, b: &i32) -> bool {
a as *const _ == b as *const _
}
另一个用例是外部函数接口 (FFI)。如果包装一个将原始指针作为参数的 C 函数,则无法将它们提供给函数。这实际上是不安全的(就像整个 FFI 业务一样),因为该函数可能会取消引用指针。这意味着您有责任确保指针有效、保持有效并且不会被多次释放。
最后,原始指针用于优化。例如,切片迭代器使用原始指针作为内部状态。这比索引更快,因为它们避免了迭代期间的范围检查。但是,就编译器而言,它也是不安全的。库作者需要特别注意,因此使用原始指针进行优化总是有引入通常在 rust 中没有的内存错误的风险。
总之,原始指针的三个主要用途是:
- “只是数字”-您永远不会访问它们指向的内存。
- FFI - 你在 Rust 之外传递它们。
- 内存映射 I/O - 要触发 I/O 操作,您需要访问固定地址的硬件寄存器。
- 性能 - 它们可能比其他选项更快,但编译器不会强制执行安全性。
至于什么时候应该使用原始指针,前三点是直截了当的:你会知道它们什么时候应用,因为你必须这样做。最后一点更微妙。与所有优化一样,只有在收益超过使用它们的努力和风险时才使用它们。
一个不使用原始指针的反例是其他指针类型(&
引用、、、、Box
)Rc
完成Arc
工作时。