Rust 中的指针和引用有什么区别?
What are the differences between a pointer and a reference in Rust?
Rust中的指针*
和引用&
是同一个表示(都是表示一段数据的内存地址)
虽然在编写代码时有什么实际区别?
将 C++ 代码移植到 Rust 时,它们是否可以安全地替换(C++ 指针 --> Rust 指针,C++ 引用 --> Rust 引用)?
Rust 引用只是一个指针,但编译器赋予它们借用语义。当您对对象进行不可变引用时,编译器会确保在派生引用消失之前您无法修改该对象。
尽可能使用引用,必须使用指针。如果您不执行超出编译器可验证范围的 FFI 或内存管理,则不需要使用指针。
引用和指针都存在两种变体。有共享引用 &
和可变引用 &mut
。有 const 指针 *const
和 mut 指针 *mut
(映射到 C 中的 const 和非常量指针)。然而,引用的语义与指针的语义完全不同。
引用在一个类型和整个生命周期内都是通用的。共享引用以长格式写成 &'a T
(其中 'a
和 T
是参数)。 lifetime参数在很多情况下可以是omitted。编译器使用生命周期参数来确保引用的寿命不会超过借用的有效期。
指针没有生命周期参数。因此,编译器无法检查特定指针是否有效。这就是为什么取消引用指针被认为是 unsafe
.
当您创建对象的共享引用时,冻结对象(即对象在共享引用存在时变得不可变),除非对象使用某种形式的内部可变性(例如使用 Cell
、RefCell
、Mutex
或 RwLock
)。但是,当您有一个指向对象的 const 指针时,该对象可能仍会在指针处于活动状态时发生变化。
当您有一个对象的可变引用时,您保证可以通过该引用独占访问该对象。访问该对象的任何其他方式要么暂时禁用,要么无法实现。例如:
let mut x = 0;
{
let y = &mut x;
let z = &mut x; // ERROR: x is already borrowed mutably
*y = 1; // OK
x = 2; // ERROR: x is borrowed
}
x = 3; // OK, y went out of scope
Mut 指针没有这样的保证。
引用不能为空(很像 C++ 引用)。指针可以为空。
指针可以包含适合 usize
的任何数值。初始化一个指针不是unsafe
;只是取消引用它。另一方面,产生无效引用被视为 undefined behavior,即使您从未取消引用它也是如此。
如果您有 *const T
,您可以使用 as
将其自由转换为 *const U
或 *mut T
。你不能用引用来做到这一点。但是,您可以使用 as
转换对指针的引用,并且可以通过取消引用指针(同样是 unsafe
)将指针“升级”为引用,然后借用该位置使用&
或 &mut
。例如:
use std::ffi::OsStr;
use std::path::Path;
pub fn os_str_to_path(s: &OsStr) -> &Path {
unsafe { &*(s as *const OsStr as *const Path) }
}
在 C++ 中,引用是“自动解除引用的指针”。在 Rust 中,您通常仍然需要显式取消引用引用。使用 .
运算符时例外:如果左侧是引用,编译器将自动取消引用它(必要时递归!)。但是,指针不会自动解除引用。这意味着如果你想解引用和访问一个字段或方法,你需要写成(*pointer).field
或(*pointer).method()
。 Rust 中没有 ->
运算符。
Rust中的指针*
和引用&
是同一个表示(都是表示一段数据的内存地址)
虽然在编写代码时有什么实际区别?
将 C++ 代码移植到 Rust 时,它们是否可以安全地替换(C++ 指针 --> Rust 指针,C++ 引用 --> Rust 引用)?
Rust 引用只是一个指针,但编译器赋予它们借用语义。当您对对象进行不可变引用时,编译器会确保在派生引用消失之前您无法修改该对象。
尽可能使用引用,必须使用指针。如果您不执行超出编译器可验证范围的 FFI 或内存管理,则不需要使用指针。
引用和指针都存在两种变体。有共享引用 &
和可变引用 &mut
。有 const 指针 *const
和 mut 指针 *mut
(映射到 C 中的 const 和非常量指针)。然而,引用的语义与指针的语义完全不同。
引用在一个类型和整个生命周期内都是通用的。共享引用以长格式写成 &'a T
(其中 'a
和 T
是参数)。 lifetime参数在很多情况下可以是omitted。编译器使用生命周期参数来确保引用的寿命不会超过借用的有效期。
指针没有生命周期参数。因此,编译器无法检查特定指针是否有效。这就是为什么取消引用指针被认为是 unsafe
.
当您创建对象的共享引用时,冻结对象(即对象在共享引用存在时变得不可变),除非对象使用某种形式的内部可变性(例如使用 Cell
、RefCell
、Mutex
或 RwLock
)。但是,当您有一个指向对象的 const 指针时,该对象可能仍会在指针处于活动状态时发生变化。
当您有一个对象的可变引用时,您保证可以通过该引用独占访问该对象。访问该对象的任何其他方式要么暂时禁用,要么无法实现。例如:
let mut x = 0;
{
let y = &mut x;
let z = &mut x; // ERROR: x is already borrowed mutably
*y = 1; // OK
x = 2; // ERROR: x is borrowed
}
x = 3; // OK, y went out of scope
Mut 指针没有这样的保证。
引用不能为空(很像 C++ 引用)。指针可以为空。
指针可以包含适合 usize
的任何数值。初始化一个指针不是unsafe
;只是取消引用它。另一方面,产生无效引用被视为 undefined behavior,即使您从未取消引用它也是如此。
如果您有 *const T
,您可以使用 as
将其自由转换为 *const U
或 *mut T
。你不能用引用来做到这一点。但是,您可以使用 as
转换对指针的引用,并且可以通过取消引用指针(同样是 unsafe
)将指针“升级”为引用,然后借用该位置使用&
或 &mut
。例如:
use std::ffi::OsStr;
use std::path::Path;
pub fn os_str_to_path(s: &OsStr) -> &Path {
unsafe { &*(s as *const OsStr as *const Path) }
}
在 C++ 中,引用是“自动解除引用的指针”。在 Rust 中,您通常仍然需要显式取消引用引用。使用 .
运算符时例外:如果左侧是引用,编译器将自动取消引用它(必要时递归!)。但是,指针不会自动解除引用。这意味着如果你想解引用和访问一个字段或方法,你需要写成(*pointer).field
或(*pointer).method()
。 Rust 中没有 ->
运算符。