搭建 Rust 环境
1、安装 Rust
使用 rustup.rs 中给出的方法,根据自身操作系统进行安装即可

2、 选择开发工具
选择一:Clion + 插件形式
推荐插件:
Rust: 提供 Rust 语言相支持

选择二:vscode + 插件形式
推荐插件:
rust-analyzer: 实时编译和分析 Rust 代码,提示代码中的错误,并对类型进行标注
rust syntax: 为代码提供语法高亮
crates: 帮助分析当前项目的依赖是否是最新版本
Even Better TOML: Rust 使用 toml 做项目的配置管理,提供 toml 语法高亮及检查 toml 文件中的错误
rust test lens: 可以帮你快速运行某个 Rust 测试

# 变量和函数

Rust 支持类型推导
在编译器能够推导类型的情况下,可以省略变量类型,但是常量和静态变量必须声明变量类型

let var1 = "var1"; // 没有显示指定变量的类型,编译器会自行进行推导
  const CONST_VAL: &str = "const value"; // 常量必须声明变量类型
  static STATIC_VAL: &str = "static value"; // 静态变量也必须声明变量类型
  // 常量和静态变量不进行类型声明会报类型丢失的错误
  // const NOT_DECLEAR: = ""; // Missing type for `const` item

Rust 变量默认是不可变的
可以添加 mut 关键字让变量具备可变性。
当你使用 mut 却没有修改变量,Rust 编译器会友好地提示你移除不必要的 mut。

let immutable_name = "alice";
    // immutable_name = "bob"; // Cannot assign twice to immutable variable [E0384]
    
    let mut mutable_name = "andy";
    mutable_name = "bob";

Rust 变量命名
Rust 变量支持英文、数字和下划线 (_),且只能以下划线和英文开头
以下划线开头的变量,表示暂时未被使用的变量
Rust 也支持使用非 ASCII 字符进行变量命名

Rust 中的函数
在 Rust 下,函数是一等公民,可以作为参数或返回值

//apply 函数有两个参数:一个是 value 类型是 i32,
// 另一个是 func 类型是 fn (i32) -> i32,它表明 func 参数接受一个函数作为参数,
// 这个传入参数必须满足:参数只有一个,且类型为 i32,返回值类型也是 i32
fn apply(value: i32, func: fn(i32) -> i32) -> i32 {
  func(value) 
}
fn square(value: i32) -> i32 {
  value * value
}
fn cube(value: i32) -> i32 {
  value * value * value
}
fn main (){
  println!("apply square: {}", apply(2, square)); // apply square: 4
  println!("apply square: {}", apply(2, cube)); // apply cube: 8
}

Rust 参数和返回值
Rust 函数参数的类型和返回值的类型都必须显式定义,如果没有返回值可以省略,返回 unit 类型
函数内部如果提前返回,需要用 return 关键字,否则最后一个表达式就是其返回值
如果最后一个表达式后添加了;分号,隐含其返回值为 unit 类型

use std::f64::consts::PI;
fn pi() -> f64 {
    PI
}
fn not_pi() {
    PI;
}
fn main() {
    let is_pi = pi();
    let is_unit1 = not_pi();
    let is_unit2 = {
        pi();
    };
    println!("is_pi: {:?}, is_unit1: {:?}, is_unit2: {:?}", is_pi, is_unit1, is_unit2);
}

# 数据结构

用 struct 定义结构体,用 enum 定义标签联合(tagged union),
还可以像 Python 一样随手定义元组 (tuple) 类型。

#[derive(Debug)]
enum Gender {
    Unspecified = 0,
    Female = 1,
    Male = 2,
}
#[derive(Debug, Copy, Clone)]
struct UserId(u64);
#[derive(Debug, Copy, Clone)]
struct TopicId(u64);
#[derive(Debug)]
struct User {
    id: UserId,
    name: String,
    gender: Gender,
}
#[derive(Debug)]
struct Topic {
    id: TopicId,
    name: String,
    owner: UserId,
}
// 定义聊天室中可能发生的事件
#[derive(Debug)]
enum Event {
    Join((UserId, TopicId)),
    Leave((UserId, TopicId)),
    Message((UserId, TopicId, String)),
}
fn main() {
    let alice = User { id: UserId(1), name: "Alice".into(), gender: Gender::Female };
    let bob = User { id: UserId(2), name: "Bob".into(), gender: Gender::Male };
    let topic = Topic { id: TopicId(1), name: "rust".into(), owner: UserId(1) };
    let event1 = Event::Join((alice.id, topic.id));
    let event2 = Event::Join((bob.id, topic.id));
    let event3 = Event::Message((alice.id, topic.id, "Hello world!".into()));
    println!("event1: {:?}, event2: {:?}, event3: {:?}", event1, event2, event3);
}

说明
1、Gender: 一个枚举类型,在 Rust 下,使用 enum 可以定义类似 C 的枚举类型

2、UserId/TopicId: struct 的特殊形式,称为元组结构体。它的域都是匿名的,可以用索引访问,适用于简单的结构体

3、User/Topic: 标准的结构体,可以把任何类型组合在结构体里使用

4、Event: 标准的标签联合体,它定义了三种事件:Join、Leave、Message。每种事件都有自己的数据结构

# 控制语句

条件语句

fn main () {
    let age: i32 = 30_i32;
    if age < 18 {
        println!("teenager");
    }else {
        println!("adult");
    }
}

循环语句
Rust 支持三种循环,分别是死循环 loop,条件循环 while,以及对迭代器的循环 for
循环可以使用 break 提前终止,或者使用 continue 跳到下一轮循环
和 C 语言类似,循环支持打标签(不推荐使用)

fn main () {
    let mut n: i32 = 5;
    loop {
        if n == 0 {
            break;
        }
        print!("loop ");
        n = n - 1;
    }
    println!();
    
    for i in 0..5 {
        print!("{} ", i);
    }
    println!();
    n = 5;
    while n != 0 {
        print!("{} ", n);
        n = n - 1;
    }
}

模式匹配
match 关键字用于模式匹配,它可以匹配 struct/enum 中部分或者全部内容
可以直接对 enum 内层的数据进行匹配并赋值
可以使用 _ 符号进行缺省匹配

// 下面使用了 Debug 派生宏 用于简化 struct/enum 输出
#[derive(Debug)]
struct CountryName (String);
#[derive(Debug)]
struct CountryRegion (String);
#[derive(Debug)]
struct OtherInfo {
    population: f32,
}
#[derive(Debug)]
enum Country {
    China(CountryName, CountryRegion, OtherInfo),
    Japan(CountryName, CountryRegion, OtherInfo),
    Canada(CountryName, CountryRegion, OtherInfo),
}
fn print_country_info(country: &Country){
    match country{
        Country::China(name, region, other_info) => {
            println!("country name: {:?}, country region: {:?}, other info: {:?}", name, region, other_info);
        },
        Country::Japan(name, region, _) => {
            println!("country name: {:?}, country region: {:?}", name, region);
        },
        Country::Canada(name, _, _) => {
            println!("country name: {:?}", name);
        }
    };
}
fn main () {
    let china = Country::China(CountryName("中国".into()), CountryRegion("亚洲".into()), OtherInfo{
        population: 14.13_f32,
    });
    let japan = Country::Japan(CountryName("日本".into()), CountryRegion("亚洲".into()), OtherInfo{
        population: 1.26_f32,
    });
    let canada = Country::Canada(CountryName("加拿大".into()), CountryRegion("北美洲".into()), OtherInfo{
        population: 0.3820_f32,
    });
    // 输出:country name: CountryName ("中国"), country region: CountryRegion ("亚洲"), other info: OtherInfo { population: 14.13 }
    print_country_info(&china);
    // 输出:country name: CountryName ("日本"), country region: CountryRegion ("亚洲")
    print_country_info(&japan);
    // 输出:country name: CountryName ("加拿大")
    print_country_info(&canada);
}

# 错误处理

Rust 没有沿用 C++/Java 等语言使用的异常处理方式,而是借鉴 Haskell,把错误封装在 Result<T, E> 类型中
同时提供了?操作符来传播错误,Result<T, E> 类型是一个泛型数据结构,T 代表成功执行返回的结果类型,E 代表错误类型
?操作符只能用在返回类型为 Result<T, E> 和 Option<T> 的函数当中

// 在 Cargo.toml 配置文件中的 dependencies 下添加如下依赖
// reqwest = {version = "0.11.0", features = ["blocking"] }
// html2md = "0.2.0"
use std::fs;
//main 函数现在返回一个 Result
fn main() -> Result<(), Box<dyn std::error::Error>> {
    let url = "https://www.rust-lang.org/";
    let output = "rust.md";
    println!("Fetching url: {}", url);
    let body = reqwest::blocking::get(url)?.text()?;
    println!("Converting html to markdown...");
    let md = html2md::parse_html(&body);
    fs::write(output, md.as_bytes())?;
    println!("Converted markdown has been saved in {}.", output);
    Ok(())
}
更新于 阅读次数