Nullable 和 Option

在 C# 中,null 通常用来表示不存在、缺失或逻辑上未初始化的值。例如:

int? some = 1;
int? none = null;

Rust 没有 null 从而也就没有启用可为 null 的上下文的说法。作为替代,可选的或不存在的值用 Option<T> 表示。上面的 C# 代码的 Rust 等价实现是:

let some: Option<i32> = Some(1);
let none: Option<i32> = None;

Rust 的 Option<T> 实际上和 F# 的 'T option 一致。

带有可选项的控制流

在 C# 中,你可能会使用 if/else 语句来控制带有可为 null 值时的程序流。

uint? max = 10;
if (max is { } someMax)
{
    Console.WriteLine($"The maximum is {someMax}."); // The maximum is 10.
}

在 Rust 中你可以用模式匹配取得同样的行为:

使用 if let 甚至会更简单:

let max = Some(10u32);
if let Some(max) = max {
    println!("The maximum is {}.", max); // The maximum is 10.
}

Null 条件运算符

C# 的 Null 条件运算符(?.?[])使得处理 null 更人性化。在 Rust 中,它们最好的替代就是使用 map 方法。下面的代码段展示了对照:

string? some = "Hello, World!";
string? none = null;
Console.WriteLine(some?.Length); // 13
Console.WriteLine(none?.Length); // (blank)
let some: Option<String> = Some(String::from("Hello, World!"));
let none: Option<String> = None;
println!("{:?}", some.map(|s| s.len())); // Some(13)
println!("{:?}", none.map(|s| s.len())); // None

Null 合并运算符

Null 合并运算符(??)常用于当一个可为 null 的值为 null 时默认为另一个值:

int? some = 1;
int? none = null;
Console.WriteLine(some ?? 0); // 1
Console.WriteLine(none ?? 0); // 0

在 Rust 中,你可以通过 unwrap_or 来取得同样的行为:

let some: Option<i32> = Some(1);
let none: Option<i32> = None;
println!("{:?}", some.unwrap_or(0)); // 1
println!("{:?}", none.unwrap_or(0)); // 0

备注:如果默认值的计算很昂贵,你可以使用 unwrap_or_else 替代。它接收闭包作为参数,因此可以懒加载默认值。

Null 包容运算符

Null 包容运算符(!)在 Rust 中没有对应的构造,因为在 C# 中,它也仅仅是影响编译器的静态分析。在 Rust 中,不需要它这样的存在。