搭建 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(()) | |
} |