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