十九. rust 高级特性
19.1 不安全rust
目前为止讨论过的代码都有 Rust 在编译时会强制执行的内存安全保证。然而,Rust 还隐藏有第二种语言,它不会强制执行这类内存安全保证:这被称为 不安全 Rust(unsafe Rust)。它与常规 Rust 代码无异,但是会提供额外的超能力。
所谓的不安全是指,编译器无法确保足够的安全;
如果使用不安全的rust
unsafe: unsafe
关键字只是提供了那五个不会被编译器检查内存安全的功能,因此保持unsafe 代码快的足够小非常与必要;
不安全包含那些方面;
- 解引用裸指针 | Dereference raw pointers
- 调用不安全的函数或方法 | Call
unsafe
functions - 访问或修改可变静态变量 |Mutate
static
s (includingextern
al ones) - 实现不安全 trait |Implement
unsafe
trait
s - 访问
union
的字段 | Access fields ofunion
s
解决不安全的方式:将不安全代码封装进一个安全的抽象并提供安全 API
19.1.1 解引用裹指针
裸指针是不可变或可变的,分别写作 *const T
和 *mut T
裸指针与引用和智能指针的区别在于:
- 允许忽略借用规则,可以同时拥有不可变和可变的指针,或多个指向相同位置的可变指针
- 不保证指向有效的内存
- 允许为空
- 不能实现任何自动清理功能
//可以在安全代码中 创建 裸指针,只是不能在不安全块之外 解引用 裸指针
let mut num = 5;
let r1 = &num as *const i32;//这里使用as
将不可变和可变引用强转为对应的裸指针类型
let r2 = &mut num as *mut i32;//这里使用as
将不可变和可变引用强转为对应的裸指针类型
//unsafe 代码
fn main() {
println!("{:p}", &5);
//可以在安全代码中 创建 裸指针,只是不能在不安全块之外 解引用 裸指针
let mut num = 5;
let r1 = &num as *const i32;
let r2 = &mut num as *mut i32;
unsafe {//解引用裹指针
println!("r1 = {:?}", *r1);
println!("r2 = {:?}", *r2);
}
// let address = 0x012345usize;
// let r = address as *const i32;
// print!("{:p}",r);
}
19.1.2 Call unsafe
functions
fn main() {
unsafe {//调用不安全的方法
dangerous();
}
}
unsafe fn dangerous() {}
19.1.3 使用extend 调用外部代码
//unsafe 代码
extern "C" {
fn abs(input: i32) -> i32;
}
fn main() {
unsafe {
// 调用外部函数
println!("{}", abs(-10));//10
}
}
19.1.4 Mutate static
s (including extern
al ones)
static HELLO_WORLD: &str = "Hello, world!";
static mut COUNT: u32 = 0; //不变静态变量
fn add_to_count(inc: u32) {
unsafe {
//修改可变静态变量
COUNT += inc;
}
}
fn main() {
println!("{}", HELLO_WORLD);
add_to_count(100); //调用函数
unsafe {
println!("COUNT: {}", COUNT); /*访问可变静态变量*/ // COUNT: 100
}
}
19.1.5 Implement unsafe
trait
s
unsafe trait Foo { // methods go here }
unsafe impl Foo for i32 { // method implementations go here }
19.1.6 Access fields of union
s
#[repr(C)]
union Myunion {
f1: u32,
f2: f32,
}
fn main() {
let u = Myunion { f1: 100 };
//访问联合体字段
let f = unsafe { u.f1 };
print!("{}", f);//100
}
19.2 高级trait (后续继续研究)
关联类型(associated types)是一个将类型占位符与 trait 相关联的方式,这样 trait 的方法签名中就可以使用这些占位符类型
type :占位符类型(类型占位符);
当使用泛型类型参数时,可以为泛型指定一个默认的具体类型。如果默认类型就足够的话,这消除了为具体类型实现 trait 的需要。为泛型类型指定默认类型的语法是在声明泛型类型时使用 <PlaceholderType=ConcreteType>
。】
这种情况的一个非常好的例子是用于运算符重载。运算符重载(Operator overloading)是指在特定情况下自定义运算符(比如 +
)行为的操作。
use std::ops::Add;
#[derive(Debug, PartialEq)]
struct Mystruct {
x: u32,
y: u32,
}
impl Add for Mystruct {
type Output = Mystruct;
fn add(self, other: Mystruct) -> Mystruct {
Mystruct {
x: self.x + other.x,
y: self.y + other.y,
}
}
}
fn main() {
assert_eq!(
Mystruct { x: 1, y: 2 }.add(Mystruct { x: 3, y: 4 }),
Mystruct { x: 4, y: 6 }
);
}
19.3 高级类型
Rust 还提供了声明 类型别名(type alias)的能力
fn main(){
type kind = u32;
let x = 5;
let y: kind = 10;
println!("x is {}, y is {}", x, y);
}
因为 Rust 需要知道例如应该为特定类型的值分配多少空间这样的信息其类型系统的一个特定的角落可能令人迷惑:这就是 动态大小类型(dynamically sized types)的概念。这有时被称为 “DST” 或 “unsized types”,这些类型允许我们处理只有在运行时才知道大小的类型。
fn generic<T: Sized>(t: T) {
// --snip--
}
fn generic<T: ?Sized>(t: &T) {
// --snip--
}?Sized
trait bound 与 Sized
相对;也就是说,它可以读作 “T
可能是也可能不是 Sized
的”。这个语法只能用于 Sized
,而不能用于其他 trait;