迁移难度说明:
- 
⭐️ —— 简单,几乎无迁移成本 
- 
⭐️⭐️ —— 适中,稍微注意下两者差异即可 
- 
⭐️⭐️⭐️ —— 较难,需要一定的编码经验,可通过借助编译器的错误提示适应 
超过 ⭐️ 的难度,可重点注意「迁移难点」高亮块。
若有任何疑问或错漏,欢迎随时交流指正。
介绍
Rust 和 Swift 一样都是现代化的年轻编程语言,语言特性上都借鉴了很多不同语言的优点,语法上有诸多相似的地方,不过诞生的背景和目标不一样,所以也有差异很大的地方,有着不同的应用场景。
Swift 诞生在 Apple,2014 年发布 1.0 版本,在 WWDC 2015 开发者大会宣布开源,并于同年发布 2.0 版本。因此,Swift 最初的目标基本就是取代 Objective-C 成为苹果平台的下一代官方编程语言,在安全、快速这样的基础上,致力于易学、易上手,以促进平台应用的进一步繁荣。之后的开源并推及到 Linux 平台和服务端,基本也是这个目标的延展。
Rust 诞生于 Mozilla,按 1.0 版本发布的话,比 Swift 还年轻 1 岁,2015 年发布的 1.0 版本。Mozilla 开源背景和历史,以及在社区上的经验和积累是非常丰富的,因此 Rust 从最初就由良好的开源社区驱动,目标是成为安全可靠且高性能的系统级编程语言。既然是系统级语言,则对标的是 C 和 C++,无运行时和垃圾回收,确保性能的同时,能够方便地进行跨平台和嵌入式开发。
Rust 中最大的思维转换是变量的所有权和生命周期,一种几乎其他编程语言都不太涉及的内存管理机制。除此之外,对于熟悉 Swift 的开发者,在通用语言层面和编程范式层面并没有太大差异。
因此,作为一名从 iOS 转 Rust SDK 的开发者,我将结合 Swift 的以往使用经验以及对 Rust 的学习爬坑体会,从客户端工程师的视角,通过以下这些方面和组织结构对两门语言进行对比学习,突出重难点,帮助大家一起基于 Swift 的经验快速向 Rust 迁移。
基础篇
变量绑定
可变性
迁移成本:⭐️
Rust 和 Swift 都默认推荐不可变优先,并明确声明可变性。
// Rust
let x = 666; 
// 默认不可变
// x = 999; <- 编译会报错
let mut y = 233; // let mut 明确声明可变性
y = 23333; // <- 编译通过
// Swift
let x = 666
var y = 233
作用域和遮蔽(shadowing)
迁移成本:⭐️
Rust 和 Swift 一样,变量绑定的作用域都被限定在一个代码块中。
// Rust
fn main() {
  let long_lived_binding = 1; // 作用域为整个 main 函数
  
  {
    let short_lived_binding = 2; // 仅作用域当前代码块中
    println!("{}", short_lived_binding); // 打印 2
  }
  // 报错:cannot find value `short_lived_binding` in this scope
  println!("{}", short_lived_binding);
  
  println!("{}", long_lived_binding); // 打印 1
}
// Swift
func main() {
    let longLivedBinding = 1
    do {
        let shortLivedBinding = 2
        print(shortLivedBinding)
    }
          // 报错:Use of unresolved identifier 'shortLivedBinding'
    print(shortLivedBinding)
    print(longLivedBinding) // 打印 1
}
和 Swift 有所差别的是, Rust 还支持变量遮蔽,Swift 仅在可选型拆包时支持变量遮蔽。
// Rust
let x = 1;
let x = 2; // 此时 x 的绑定会遮蔽上一个 `x` 的绑定
println!("{}", x); // 打印 2
// Swift
let x = 1
let x = 2 // 报错:Definition conflicts with previous value
// 仅支持可选型的拆包“遮蔽”
let x: Int? = 1
print(x) // 打印:Optional(1)
if let x = x {
    print(x) // 打印 1, 此时 x 不再是可选型而是实际内容
}
类型系统
关键点:Rust 和 Swift 在理念上,均面向类型安全设计,通过强大的类型系统,在编译时保障软件安全,尽可能避免运行时崩溃或未定义行为。
强类型&静态类型
迁移成本:⭐️
和 Swift 一样,Rust 也是一门强类型+静态类型的编译型语言。
- 
强类型:一旦变量被指定类型,除非强制转换,否则始终为该类型 
- 
静态类型:编译期间执行类型检查,避免类型错误 
// Rust
let x: u8 = 200;
let y: u8 = 100;
// Swift
let x: Int = 200
let y: Int = 100
类型推导
迁移成本:⭐️
Rust 和 Swift 均具有类型推导能力
// Rust
let x = 2.0; // 自动类型推导:f64
let y: f32 = 3.0; // 显示声明类型:f32
// Swift
let x = 2.0 // Double
let y: Float = 3.0 // Float
类型转换(Type cast)
迁移成本:⭐️
与 Swift 类似,Rust 不提供原生类型之间的隐式类型转换,而是使用 as 关键字进行显式转换。
// Rust
let decimal = 3.14_f32;
// 不支持隐式类型转换,报错:mismatched types
let integer: u8 = decimal; 
let integer = decimal as u8; // 支持显式转换
// Swift
let decimal = 3.14
// 不支持隐式类型转换,报错:Cannot convert value of type 'Double' to specified type 'Int'
let integer: Int = decimal
let integer = Int(decimal)
let thing1: Int = Int(89.0) as Int
let thing2: Float = 1 as Float
差异注意点:
- 
Rust: - as可能丢失精度(类比 C/C++ 中的隐式转换),且仅支持原始类型的显式声明转换
- 由于没有 class 也就没有继承,因此无需 down_cast 的场景
- 对于自定义类型转换,一般采用实现 From trait和Into trait的方式
- as关键字还可与- use配合使用:- use std::mem as memory;
 
- 
Swift: - as仅在不丢失精度的情况下可用,其他情况须使用类型构造方法。
- as!和- as?更多的使用场景是 class 继承中的 down_cast。
 
值语义 v.s 引用语义
迁移成本:⭐️⭐️
Rust 中自定义类型只有 enum 和 struct,和 Swift 一样均默认为值语义。无 class 类型,因此引用语义需通过& **借用(Borrowing)**或配合智能指针显式声明。
// Rust
// 注意 `let mut`、`&mut` 的使用
#[derive(Debug)]
struct Foo {
    x: u8
}
fn change_foo_val(foo: &mut Foo, val: u8) {
    foo.x = val;
}
fn main() {
    let mut foo = Foo { x: 1 };
    change_foo_val(&mut foo, 9);
    
    println!("foo: {:?}", foo); // 输出:foo: Foo { x: 9 }
}
// Swift
// 注意 `var`、`inout`、`&` 的使用
struct Foo {
    var x: Int
}
func changeFooVal(_ foo: inout Foo, to val: Int) {
    foo.x = val
}
var foo = Foo(x: 1)
changeFooVal(&foo, to: 9)
print("foo: \(foo)") // 输出:foo: Foo(x: 9)`
迁移难点:
与 Swift 中的值语义默认支持 CoW(Copy on Write)不同,Rust 中除非显式声明其Copy trait(可以简单理解为声明后就支持 CoW 了),否则默认是「所有权转移」语义,这在后面「内存管理」一节中的所有权机制中会详细介绍。
值语义
Swift 中有结构体(struct)和枚举(enum)两种值语义的自定义类型,Rust 中也类似。
结构体(Struct)
// Rust
// 声明
struct Point {
    x: f32,
    y: f32,
}
// 使用`impl`关键字来实现方法
impl Point {
  fn new() -> Self {
    Self { x: 0.0, y, 0.0 }
  }
}
// 初始化
let point = Point { x: 0.5, y: 0.5 };
let oringin = Point::new();
// 支持解构
let Point { x: the_x, y: the_y } = point;
println!("{}, {}", the_x, the_y); // 打印:0.5, 0.5
// Swift
struct Point {
    let x: Float
    let y: Float
}
let point = Point(x: 0.5, y: 0.5)
枚举(Enum)
与 Swift 类似,Rust 中枚举也支持关联值
// Rust
enum CustomError {
  CustomError1,
  CustomError2,
  CustomError3
}
// 关联值枚举
enum CustomError {
  CustomError1(&str),
  CustomError2(u32),
  CustomError3(&str, u32)
}
// Swift
enum CustomError {
  case customError1
  case customError2
  case customError3
}
// 关联值枚举
enum CustomError {
  case customError1(String)
  case customError2(UInt)
  case customError3(String, UInt)
}
引用语义
Swift 中有类(class)来声明引用语义的自定义类型。但在 Rust 中没有类的概念,要使用引用语义,需要在值语义类型之上通过标准库中的 Box<T> 、 Rc<T> 、Arc<T> 等智能指针进行「封装」。
见「智能指针」小节
数据类型
迁移成本:⭐️⭐️
原始类型(Primitive Types)
Rust 与 Swift 的原始数据类型基本一致,仅以整型示例,其他基本都无缝转换。
组合类型(Compund Types)
元组(Tuples)
// Rust
let tup: (i32, f64, u8) = (500, 6.4, 1);
// 模式匹配
let (x, y, z) = tup;
println!("The value of y is: {}", y);
// Swift
let tup: (UInt32, Double, UInt8) = (500, 6.4, 1)
// 模式匹配
let (x, y, z) = tup
print("The value of y is: \(y)")
数组(Arrays)
// Rust
let a = [1, 2, 3, 4, 5];
// 类型声明
// 💡:5 表明该数组为 5 个元素,编译时静态确定,属于类型的一部分
let b: [i32; 5] = [1, 2, 3, 4, 5];
let c = [3; 5]; // 等价:let c =[3, 3, 3, 3, 3]
// 元素访问
let first = a[0];
let second = a[1];
// 数组访问越界
// 编译错误:error: this operation will panic at runtime
let six = a[5]; 
//       ^^^^ index out of bounds: the length is 5 but the index is 5
// Swift
let a = [1, 2, 3, 4, 5]
// 类型声明
let b: Array<Int32> = [1, 2, 3, 4, 5]
let b2: [Int32] = [1, 2, 3, 4, 5]
// 元素访问
let first = a[0]
let second = a[1]
// 数组访问越界
// 运行时崩溃:Fatal error: Index out of range
let six = a[5]
迁移难点:
- 与 Swift 中的数组支持动态扩容不同,Rust 中的数组与 C 中的数组更类似,仅支持静态大小。size 是其类型的一部分,相同类型不同大小的数组是不同的数据类型,且不支持动态扩容。因此,Rust 可以很好地避免运行时数组访问越界。
- 若需要支持动态扩容,使用 Vec<T>类型
关联数据类型(Related Data)
结构体(Structs)
Rust 与 Swift 中的 struct 在语法层面十分类似。只需注意细微的一些差异即可:
- 
属性声明不区分可变性,与结构体变量整体共用可变性; 
- 
默认构造方法属于 C 系风格,一般会额外自定义 fn new 关联方法(可选); 
- 
Rust 支持「元组风格结构体」(tuple struct) 
- 
Rust 支持「单位类型结构体」(unit-like) 
- 
Rust 的结构体在关联存储引用类型时,须格外注意所有权和生命周期问题 
常规使用
// Rust
// 定义声明
struct User {
    active: bool,
    username: String,
    email: String,
    sign_in_count: u64,
}
// 初始化构造
let mut user1 = User {
    email: String::from("someone@example.com"),
    username: String::from("someusername123"),
    active: true,
    sign_in_count: 1,
};
// Getter&Setter 均支持点语法
user1.email = String::from("anotheremail@example.com");
// Swift
struct User {
    var active: Bool
    var username: String
    var email: String
    var sign_in_count: UInt64
}
// 初始化构造
var user1 = User(active: true, username: "someusername123", email: "someone@example.com", sign_in_count: 1)
// Getter & Setter
user1.email = "anotheremail@example.com"
元组结构体(Tuple struct)
// Rust
struct Color(i32, i32, i32);
struct Point(i32, i32, i32);
fn main() {
    let black = Color(0, 0, 0);
    let origin = Point(0, 0, 0);
}
单位结构体(Unit struct)
// Rust
struct AlwaysEqual;
fn main() {
    let subject = AlwaysEqual;
}
所有权与生命周期标注
再一次触及到 Rust 关键点,先有个印象,后面内存管理一节中的所有权机制中会继续详细介绍。
生命周期标注,是为了让编译器确保结构体内部引用的数据不会早于当前结构体释放,避免悬垂指针的内存问题导致崩溃。
// Rust
// 未标注生命周期,报错
// error[E0106]: missing lifetime specifier
struct User {
    active: bool,
    username: &str,
    //        ^ expected named lifetime parameter
    email: &str,
    //        ^ expected named lifetime parameter
    sign_in_count: u64,
}
// 标注生命周期,通过编译
struct User<'a> {
    active: bool,
    username: &'a str,
    email: &'a str,
    sign_in_count: u64,
}
fn main() {
    let user1 = User {
        email: "someone@example.com",
        username: "someusername123",
        active: true,
        sign_in_count: 1,
    };
}
枚举(Enum)
// Rust
enum Message {
    Quit,
    Move { x: i32, y: i32 },
    Write(String),
    ChangeColor(i32, i32, i32),
}
impl Message {
    fn call(&self) { /*do call*/ }
}
let m = Message::Write(String::from("hello"));
m.call();
// Swift
enum Message {
    case quit
    case move(x: Int32, y: Int32)
    case write(String)
    case changeColor(Int32, Int32, Int32)
}
extension Message {
    func call() { /*do call*/ }
}
let m = Message.write("hello")
m.call()
深入学习数据类型:doc.rust-lang.org/book/ch03-0…
面向过程
控制流
迁移难度:⭐️⭐️
条件控制
Rust 和 Swift 以及绝大多数语言一样,使用 if/else 进行条件控制。
需要额外注意的点是 Rust 中一切皆表达式,不支持常规的三目运算符,也是用 if/else 表达。
// Rust
let is_super = true;
let a = if is_super { u32::MAX } else { u32::MIN };
// Swift
let isSuper = true
let a = isSuper ? Int32.max : Int32.min
循环
Rust 中循环提供 loop、while、for/in 三种方式,和 Swift 非常类似
- loop:无限循环,使用 break 退出或 continue 跳入下一个迭代
// Rust
let mut count = 0;
loop {
  count += 1;
  if count == 3 {
    continue; // 跳过后续执行直接进入下个迭代
  }
  println!("{}", count);
  if count == 5 {
    break; // 退出循环
  }
}
// swift
var count = 0
repeat {
    count += 1
    if count == 3 {
        continue
    }
    print(count)
    if count == 5 {
        break
    }
} while true
Rust 中 loop 的嵌套是支持标签注明的,break 时默认退出当前循环,可以标识标签来直接退出外层循环。
'outer: loop {
    println!("Entered the outer loop");
    'inner: loop {
        println!("Entered the inner loop");
        // 默认只是中断内部的当前循环
        //break;
        // 直接会中断外层循环
        break 'outer;
    }
    println!("This point will never be reached");
}
println!("Exited the outer loop");
除此之外,loop 还支持通过 break 返回值。
let mut counter = 0;
let result = loop {
    counter += 1;
    if counter == 10 {
        break counter * 2;
    }
};
assert_eq!(result, 20);
- 
while:条件循环,和 Swift 以及其他许多语言的 while 使用无二致,就不示例了。 
- 
forin:和 Swift 一样,支持区间和迭代的应用 
// Rust
for i in 0..5 {
    println!("{}", i);
}
// Swift
for i in 0..<5 {
    print("{}", i);
}
迁移难点:
- 理解并习惯作为一门现代语言,Rust 仍保留;作为分隔符的设计初衷,即「一切皆表达式」,这是 Rust 在控制流方面和 Swift 最大的区别与思维转换点。
模式匹配(Pattern Match)
Rust 和 Swift 一样,支持强大的模式匹配,Swift 沿用了 C 风格的 switch 关键字,Rust 是直截了当采用了 match 关键字;以及均支持 if let 进行更精简的模式匹配处理(熟悉的配方,Rust 向 Swift 取的经)
- 常规针对枚举的匹配
// Rust
#[derive(Debug)]
pub enum State {
    Running,
    Stopped,
    Sleeping,
}
fn do_something_based_on_state(curr_state: State, pid: u32) {
    match curr_state {
        State::Running => stop_running_process(pid),
        State::Stopped => restart_stopped_process(pid),
        State::Sleeping => wake_sleeping_process(pid),
    }
}
- 更丰富的匹配表达
// Rust
let x = 10;
match x {
    1 | 2 | 3 => println!("number is 1 or 2 or 3"),
    4..=10 => println!("number is between 4 and 10 inclusive"),
    x if x * x < 250 => println!("number squared is less than 250"),
    _ => println!("number didn't meet any previous condition!"),
}
// Swift
let x = 10;
switch x {
case 1, 2, 3: print("number is 1 or 2 or 3")
case 4...10: print("number is between 4 and 10 inclusive")
case x where x * x < 250: print("number squared is less than 250")
default: print("number didn't meet any previous condition!")
}
- if let 风格的精简模式匹配
// Rust
let curr_state = State::Running;
if let State::Running = curr_state {
    println!("Process is running!");
}
函数
迁移难度:⭐️
Rust 和 Swift 的函数风格十分相似,差异点是:
- 
Rust 不支持默认参数,须采用显式 Option 的方式; 
- 
Rust 不支持针对函数内/外部,对参数进行差异化命名; 
- 
Rust 的参数除非通过 &或&mut显式声明,否则默认为「所有权转移」语义- CoW(Copy on Write)语义通过 Copy trait标注支持
 
- CoW(Copy on Write)语义通过 
- 
以及(再一次),重点理解与习惯「一切皆表达式」的思维 
// Rust
fn print_labeled_measurement(value: i32, unit_label: char) {
    println!("The measurement is: {}{}", value, unit_label);
}
print_labeled_measurement(5, 'h');
// Swift
func printLabeledMeasurement(value: Int32, unitLabel: String) {
    print("The measurement is: {}{}", value, unitLabel);
}
printLabeledMeasurement(value: 5, unitLabel: "h")
面向对象
封装
迁移难度:⭐️
和 Swift 一直在尝试推荐使用值语义如出一辙,不过 Rust 更激进,仅支持通过struct 和 enum 对数据进行封装,完全摈弃 class 的概念。参考:「结构体」 & 「枚举」相关小节
继承&多态
迁移难度:⭐️⭐️
🎉 Rust** 不支持继承**。通过trait(特性,即 Swift 中 protocol)定义共享行为来复用代码,以及实现多态。采用 Swift 熟悉了面向协议的思维方式,就可以很快适应。
除此之外,和 Swift 一样,会使用泛型进行代码复用,后面会一节专门介绍。
- 定义一个特性(trait)
// Rust
pub trait Summary {
    fn summarize(&self) -> String;
}
// 支持默认实现
pub trait Summary {
    fn summarize(&self) -> String {
        String::from("(Read more...)")
    }
}
- 为不同类型实现对应的特性
// Rust
pub struct NewsArticle {
    pub headline: String,
    pub location: String,
    pub author: String,
    pub content: String,
}
impl Summary for NewsArticle {
    fn summarize(&self) -> String {
        format!("{}, by {} ({})", self.headline, self.author, self.location)
    }
}
pub struct Tweet {
    pub username: String,
    pub content: String,
    pub reply: bool,
    pub retweet: bool,
}
impl Summary for Tweet {
    fn summarize(&self) -> String {
        format!("{}: {}", self.username, self.content)
    }
}
- trait 作为多态参数
// Rust
pub fn notify(item: &impl Summary) {
    println!("Breaking news! {}", item.summarize());
}
- trait 作为多态返回值
// Rust
fn returns_summarizable() -> impl Summary {
    Tweet {
        username: String::from("horse_ebooks"),
        content: String::from(
            "of course, as you probably already know, people",
        ),
        reply: false,
        retweet: false,
    }
}
迁移难点与思维转换技巧:
- 
Rust 的 trait和 Swift 的protocol理念十分类似,可优先通过 Swift 掌握面向协议编程的一些基本概念
- 
参考资料:Swift 中的面向协议编程 
访问控制
迁移难度:⭐️
Rust 中默认访问权限是模块内(mod)可见,通过pub 关键字对外开放访问权限,可以通过“传参”进行更精细的范围控制。
pub | pub ( crate ) | pub ( self ) | pub ( super ) | pub ( in SimplePath )
pub mod outer_mod {
    pub mod inner_mod {
        // This function is visible within `outer_mod`
        pub(in crate::outer_mod) fn outer_mod_visible_fn() {}
        // Same as above, this is only valid in the 2015 edition.
        pub(in outer_mod) fn outer_mod_visible_fn_2015() {}
        // This function is visible to the entire crate
        pub(crate) fn crate_visible_fn() {}
        // This function is visible within `outer_mod`
        pub(super) fn super_mod_visible_fn() {
            // This function is visible since we're in the same `mod`
            inner_mod_visible_fn();
        }
        // This function is visible only within `inner_mod`,
        // which is the same as leaving it private.
        pub(self) fn inner_mod_visible_fn() {}
    }
    pub fn foo() {
        inner_mod::outer_mod_visible_fn();
        inner_mod::crate_visible_fn();
        inner_mod::super_mod_visible_fn();
        // This function is no longer visible since we're outside of `inner_mod`
        // Error! `inner_mod_visible_fn` is private
        //inner_mod::inner_mod_visible_fn();
    }
}
内存管理
引用计数 v.s 所有权机制
迁移难度:🌟🌟🌟
Swift 通过 ARC(自动引用计数)管理内存,Rust 通过所有权机制管理内存。
引用计数(Reference Count)
- 
通过跟踪和计算类的实例被多少属性,常量和变量所引用,来管理实例内存 
- 
确保引用计数>0时,对象不被释放;引用计数=0时,释放对象 
- 
注: 只应用于类的实例。结构体和枚举是值类型,不是引用类型,没有通过引用存储和传递; 
所有权规则(Ownership)
- 
一个值只有一个所有者(owner) 
- 
所有权(ownership)可以传递(move)给函数和其他变量 
- 
所有者负责从内存中删除数据(一旦所有者超出的作用域,则释放内存) 
- 
所有者能够对数据进行任何操作并可以对其进行修改 
// Rust
{
    let s = String::from("hello"); // s is valid from this point forward
    // do stuff with s
}                                  // 一旦超出作用域,内存释放
                                   // 不可再访问
// Swift
do {
    let s = String("hello")
    // do stuff with s
}                            // 一旦超出作用域名,内存释放
                             // 不可再访问
迁移难点与思维转换技巧:
- 
引用计数仅应用于类(class)的实例对象,除此之外,针对 Swift 中的值语义类型(结构体与枚举),就是实现了 Copy 特性的所有权机制; 
- 
重点需要理解的是所有权可转移(move): - 函数参数调用;
- 变量重新绑定;
 
- 
若实在不好理解,可以简单粗暴地视为「引用计数只能为1」,一个对象只能有唯一的变量绑定,除非 &借用,否则不可共享访问。(Copy 语义,不再是共享访问)
如何处理所有权被转移的情况([E0382])
- 显式调用 .clone() 进行克隆,对内存进行拷贝传递
#[derive(Debug, Clone)]
struct Dot {
    id: String,
    x: i32,
    y: i32
}
fn main() {
    let dot = Dot { id: "dot_1".to_string(), x: 1, y: 2 }; 
    pacman(dot.clone());
    // 再次调用,编译通过 ✅
    pacman(dot);
}
fn pacman(dot: Dot) { 
    println!("Eating {:?}", dot);
    
}
- 
对数据类型实现 Copy trait,表明该类型可以简单通过位复制创造副本 - 实现了 Copy trait 的数据类型,将默认通过「值拷贝」语义进行参数传递
 
#[derive(Debug, Clone, Copy)]
struct Dot {
    id: String,
    x: i32,
    y: i32
}
fn main() {
    let dot = Dot { id: "dot_1".to_string(), x: 1, y: 2 }; 
    pacman(dot); // 实现了 Copy trait,默认通过「值拷贝」传递
    // 再次调用,编译通过 ✅
    pacman(dot);
}
fn pacman(dot: Dot) { 
    println!("Eating {:?}", dot);
    
}
- 通过&借用(Borrow),在避免所有权转移的情况下共用数据
可选型(Optional)
迁移难度:🌟🌟
Rust 和 Swift 一样支持可选型,以便更优雅从容地表达和处理「空」的情况。
- 标准库定义与实现
// Rust
enum Option<T> {
    None,
    Some(T),
}
// Swift
public enum Optional<Wrapped> : ExpressibleByNilLiteral {
    case none
    case some(Wrapped)
    
    public init(nilLiteral: ()) {
        self = .none
    }
}
- 常规使用
// Rust
let some_number = Some(5);
let some_string = Some("a string");
let absent_number: Option<i32> = None;
// Swift
let some_number = Optional(5)
let some_string = Optional("a string")
let absent_number: Int32? = .none
// 更 Swifty 的方式
let someNumber: Int? = 5
let someString: String? = "a string"
let absentNumber: Int32? = nil
迁移难点:
- Rust 中的可选型和 Swift 相比,是去?/!语法糖版本,没有 Swift 那么方便,刚开始会不太习惯
智能指针
迁移难度:🌟🌟🌟
Box
Box<T>是最简单的引用语义的封装,在堆上分配内存后的引用指针,且仅支持有一个所有者(owner,类似 Swift 中的强引用)。
// Rust
let a = Box::new(5);
let b = a; // a 被`move`到了 b
// 报错:use of moved value: `a`
println!("{:?}", a);
Rc(refence-counting)
相比 Box,Rc<T> 稍微更像 Swift 中的引用些了,通过「浅拷贝」支持多个引用了。
// Rust
use std::rc::Rc;
let a = Rc::new([5]);
let b = a.clone(); // 相当于 Swift 中的浅拷贝
println!("{:?}", a); // 打印:[5]
Arc(Atomically Reference Counted)
Arc<T> 直译一下的话,感觉有点熟悉,「原子引用计数」,但不要将它和 Swift 中内存管理方式混淆。Arc 和 Rc 的作用差不多,但 Rc 是在单线程下使用,Arc 是在多线程下使用,因此 clone 的实现是原子性的,因此能做到线程安全。
// Rust
use std::sync::Arc;
let foo = Arc::new(vec![1.0, 2.0, 3.0]);
let a = foo.clone();
Arc 是只读引用,要使用可写引用,需要再另外使用 Mutex 来实现,类似Arc<Mutex<T>>。
迁移难点与技巧:
- 
由于 Rust 对内存的管理机制基于所有权规则,因此基本完全做到了默认使用更高效的「栈内存」,所以要开辟「堆内存」并使用引用语义,则需要借助智能指针。 
- 
可以将智能指针理解为对必须使用 class(引用语义)的场景补充
- 
不过,从客户端的视角,针对上层业务开发,使用到智能指针的频率较低,慢慢熟悉也不要紧 
结语
耐心看到这的你,开始 happy coding 吧,相信你会同时爱上 Swift & Rust 的。
- 🦀️ rustup gogo:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
参考
- Announcing Rust 1.0
- Swfit 2.0
- The Rust Programming Language - The Rust Programming Language
- From Swift To C++
- Rust - 棒棒彬的第二大脑
- ~gruberb/onetutorial - sourcehut git