Rust语法结构

Hello, Rust!

fn main() {
    println!("Hello, AwesomeProgram!");
}

数据结构

  • 通过关键字定义的数据类型和内存位置
例子解释
struct S {}使用命名字段定义一个 结构体
struct S { x: T }x 使用类型 T 的命名字段定义结构体
struct S (T);定义带有编号字段类型的"元组"结构体
struct S;定义 零大小 的单元结构体,不占空间
enum E {}定义一个 枚举
enum E { A, B(), C {} }定义枚举的变体,可以是 unit-A, tuple-B() 和struct-like C{}
enum E { A = 1 }如果变体只是与单元类似,则允许赋值,例如 FFI
union U {}用于兼容 FFI 的,类似 C union 的 unsafe 代码
static X: T = T();具有生命周期,单独内存位置的 全局变量
const X: T = T();定义 常量,使用时会复制到临时文件中
let x: T;在绑定为 x 的堆栈上分配 T 类型的字节,可赋值一次,不可变
let mut x: T;同上,但允许 可变 和可变借用
x = y;移动 yx,如果 T 没有实现 Copy,则 y 的所有权会失效,否则会保留所有权并把 y 复制一份
  • 创建和访问数据结构,以及一些符号类型
例子解释
S { x: y }创建 struct S {} 或者 useenum E::S {} ,将字段 x 设置为 y
S { x }同上,但使用本地变量 x 设置字段 x
S { ..s }填写剩余的字段 s,特别是与 Default 一起使用
S { 0: x }像下一行 S(x) 一样,但设置字段 .0 使用结构体语法
S​ (x)创建 struct S (T) 或者 useenum E::S () ,将字段 .0 设置为 x
S如果 S 是单元结构体 S,或者 useenum E::S, 去创建 S 的值
E::C { x: y }创建枚举变体 C ,上面其它的方法也可以
()空元组,包括字面值和类型,又名单元 unit
(x)带括号的表达式
(x,)单元素 元组 表达式
(S,)单元素元组类型
[S]未指定长度的 数组类型,即 slice,不能存储于栈中
[S; n]数组类型,固定长度是 n ,存储元素的类型是 S
[x; n]具有 xn 个副本的数组实例
[x, y]具有给定元素 xy 的数组实例
x[0]集合索引, 使用 usize 类型, 可以用 Index, IndexMut 来实现
x[..]同上,全范围,还有 x[a..b], x[a..=b], ... 以及如下等等
a..b左闭右开范围,如 1..3 意味着 1, 2
..b从起点开始的左闭右开范围
..=b从起点开始的,包含最右边元素的范围
a..=b包容最右边元素的范围,1..=3 意味着 1, 2, 3
a..a 开始到结尾的范围
..全范围,通常意味着整个集合
s.x命名字段的访问,如果 x 不是类型 S 的一部分,则可能会尝试去调用 Deref(取消引用)
s.0编号字段访问,用于元组类型 S (T)

引用和指针

  • 授权未拥有内存的访问权限,另外可参阅泛型和约束部分
例子解释
&S共享的引用(空间中可存放任何的 &s
&[S]包含地址和长度的指定切片引用
&str包含地址和长度的指定字符串切片引用
&mut S可变的独占引用(也包括 &mut [S]&mut dyn S, …)
&dyn T包含地址和 vtable 的指定 trait object 引用
&s共享的借用(如 地址,长度,vtable,...以及像 0x1234
&mut s可变的独占借用
*const S不可变 原生指针类型,无内存安全保证
&mut s可变 原生指针类型,无内存安全保证
&raw const s创建不经过引用的原生指针,ptr:addr_of!()
&raw mut s同上,是可变的,并且需要对未对齐的字段进行补齐
ref s通过引用绑定,成为绑定引用类型
let ref r = s;等价于 let r = &s
let S { ref mut x } = s;可变引用绑定,等价于 let x = &mut s.x;简洁的解构写法见模式匹配
*r解除一个引用 r 去访问它所指向的内容
*r = s;如果 r 是可变引用,则移动或复制 s 到目标内存
s = *r;如果 *rCopy 的话,使 s 等于任何 r 引用的副本
s = *r;如果 *r 不是 Copy 的话,将不起作用,且会移动并留下空赋值
s = *my_box;Box 的特殊例子,如果 *my_box 不是 Copy 的话,将会移出 Box 里的内容
'a生命周期的参数符号,表示在静态分析中的存活时间
&'a S只接受存有 S 类型的地址,存在 'a 或更长
&'a mut S同上,但是 S 类型地址的内容可变
struct S<'a> {}表示 S 包含生命周期 'a 的地址, S 的创建者决定生命周期 'a 的长短
trait T<'a> {}表示 impl T for SS 可能会包含地址
fn f<'a>(t: &'a T)同上,对于一个函数,调用者决定生命周期 'a 的长短
'static持续整个程序执行期间的固定生命周期

函数与方法

  • 定义代码单元及其抽象表达
例子解释
trait T {}定义一个可以实现的通用行为,特征
trait T : R {}Tsupertrait Rsubtrait,任何 S 必须在它 impl T 之前 impl R
impl S {}类型 S 的方法实现
impl T for S {}为类型 S 实现特征 T 中的方法
impl !T for S {}禁用自动派生 auto trait
fn f() {}定义一个 函数,或者一个 关联函数(在 impl 内)
fn f() -> S {}同上,返回类型 S 的值
fn f(&self) {}定义一个 方法,如在 impl S {}
struct S ​(T);可以定义一个 构造函数 fn S(x: T) -> S
const fn f() {}在编译时可用的常量 fn ,如 const X: u32 = f(Y)
async fn f() {}异步函数转换,使 f 返回一个 impl Future
async fn f() -> S {}同上,但使 f 返回一个 impl Future<Output=S>
async { x }在一个函数内使用,使 { x }impl Future<Output=X>
fn() -> S函数指针,内存持有可调用的地址
Fn() -> SCallable Trait (也可以是 FnMutFnOnce),由闭包实现
|| {} 可借用其 捕获闭包 (如,局部变量)
|x| {}闭包接受一个名为 x 的参数,主体是块表达式
|x| x + x同上,没有块表达式,只有一单个表达式
move |x| x + y 闭包获取其捕获的所有权,即 y 转移到闭包中
return || true 闭包看起来像是 logical ORs (返回一个闭包)
unsafe如果你喜欢放假前调试代码(代码的安全由你自己保证),unsafe code
unsafe fn f() {}会调用导致不安全的代码块,你必须检查必要条件
unsafe trait T {}不仔细的 T 实现可能会造成不安全的代码块,实现者必须仔细检查
unsafe { f(); }向编译器保证已经检查了必要条件
unsafe impl T for S {}保证 S 是表现良好的,可以安全在 S 上使用 T

控制流程

  • 在函数内执行控制
例子解释
while x {}Loop,当表达式 x 是真时一直运行
loop {}Loop indefinitely 直到 break,可以主动创建 break x
for x in iter {}循环遍历 迭代器 的语法糖
if x {} else {}如果表达式为真,则执行 条件分支
'label: loop {}循环标签,用于嵌套循环中的流程控制
breakBreak expression 可退出循环
break x同上,但使 x 为循环表达式的值(仅在实际的 loop
break 'label不仅要退出这个循环,还要退出标有 'label 的封闭循环
break 'label x同上,但使 x 为标有 'label 的封闭循环的值
continueContinue expression 跳转到此循环后的下一个循环迭代
continue 'label同上,但使标有 'label 的封闭循环替代本次循环
x?如果 xErrNonereturn and propagate
x.await只作用于 async 里,直到 Future 生成流或准备好 x
return x从函数中提前返回,更惯用的做法是在结尾使用表达式
f()调用 f,(如一个函数,闭包,函数指针, Fn )
x.f()调用成员函数,需要 fself&self,...作为第一个参数
X::f(x)x.f(),除非实现了 impl Copy for X {},否则 f 仅可被调用一次
X::f(&x)x.f()
X::f(&mut x)x.f()
S::f(&x)如果 x derefsSx.f() 一样, 即 x.f() 发现了 S 的方法
T::f(&x)如果 x impl Tx.f() 一样, 即 x.f() 在范围内发现了 T 的方法
X::f()调用关联函数,如 X::new()
<X as T>::f()X 调用特征方法 T::f() 的实现

代码组织

  • 拆分项目到更小的作用域内和最少的依赖
例子解释
mod m {}定义一个模块,从 {} 内部获取定义
mod m;定义一个模块,从 m.rsm/mod.rs 中获取定义
a::ba (mod, enum, …) 的内部,名命空间的路径到元素 b
::bcrate rootexternal preludeglobal path 搜索 b
crate::bcrate root 中搜索 b
self::b在当前的模块中搜索 b
super::b在上一级模块中搜索 b
use a::b;在范围内直接使用 b 而不需要 a 其余部分
use a::{b, c};同上,但是会把 bc 放到范围内
use a::b as x;b 放到范围,使用别名 x,也像这样 use std::error::Error as E
use a::b as _;b 作为匿名放到范围中,通常适用于命名冲突的特征
use a::*;a 内的所有接口放到范围中,仅推荐如果 a 是一些 prelude
pub use a::b;a::b 放到范围中,并且在这里重新导出
pub T如果上一级路径是可见的则公开 visibility
pub(crate) T最多在当前 crate 中可见
pub(super) T最多在上一级中可见
pub(self) T最多在当前模块中可见(默认和没有 pub 一样).
pub(in a::b) T最多在 a::b 中可见
extern crate a;声明对外部的 crate 依赖,仅在 use a::b
extern "C" {}FFI 声明外部依赖项和 ABI(如 "C")
extern "C" fn f() {}定义要使用 ABI("C")导出到 FFI 的函数
  • 子模块中的项目始终可以访问任何项目,无论是否为 pub

类型别名和强制转换

  • 类型的简写名称,以及将一种类型转换为另一种类型的方法
例子解释
type T = S;创建一个类型别名,即 S 的另一个名称
Selfimplementing type,如 fn new() -> Self
selffn f(self) {} 里的方法,与 fn f(self: Self) {} 一样
&self同上,但将 self 改为借用,与 f(self: &Self) 一样
&mut self同上,但将 self 改为可变借用,与 f(self: &mut Self) 一样
self: Box<Self>任意self类型 Arbitrary self type,给智能指针添加方法(my_box.f_of_self()
S as T消除类型 S 改为特征 T, 如 <S as T>::f()
S as Ruse符号中使用,导入 S 作为 R,如 use a::S as R
x as u32简单映射,可能会遭到意外的截断

宏和属性

  • 代码在实际编译之前进行展开生成
例子解释
m!()宏调用, 也可以 m!{}m![] (依赖宏的实现)
#[attr]外部属性,用于注释下面的条目
#![attr]内部属性,用于注释上一级和周围的条目

内部宏解释
$x:ty宏捕获,:... 片段为 $x 声明了允许的内容
$x宏替换,例如使用上面捕获的 $x:ty
$(x),*宏重复,例如在宏中重复零次或多次
$(x),?同上,但重复零次或一次
$(x),+同上,但重复一次或多次
$(x)<<+实际上除了分隔符 , 也可以接受这样的 <<

模式匹配

  • 在 match 或 let 表达式 以及函数参数中的模式构造
例子解释
match m {}初始化模式匹配,然后使用匹配分支
let S(x) = get();明显地,let 还可以解构成类似下面的形式
let S { x } = s;仅把 x 绑定到 s.x 的值
let (_, b, _) = abc;仅把 b 绑定到 abc.1 的值
let (a, ..) = abc;忽略 其余部分 也有作用
let (.., a, b) = (1, 2);指定绑定优先于 其余部分,这里 a1b2
let s @ S { x } = get();x 绑定到 s.x 时,s 绑定到 Spattern binding
let w @ t @ f = get();给每个 w t f 存储 get() 的三个副本结果
let Some(x) = get();如果是可辩驳模式不会起到作用,应使 if let 来代替
if let Some(x) = get() {}如果模式可被分配成分支(如,枚举变体),语法糖
while let Some(x) = get() {} 同上,{} 只要模式可被分配成分支,将继续调用 get()
fn f(S { x }: S)函数参数也像 let 一样使用,这里把 x 绑定到 f(s)s.x

*脱糖后如 match get() { Some(x) => {}, _ => () }

  • 匹配表达式中的模式匹配分支,左侧这些分支也可以在 let 表达式中找到
在匹配分支内解释
E::A => {}匹配枚举变体 A模式匹配
E::B ( .. ) => {}匹配枚举元组变体 B,任何索引的通配符
E::C { .. } => {}匹配枚举结构体变体,任何字段的通配符
S { x: 0, y: 1 } => {}匹配结构体与指定值(仅接受 ss.x 0s.y 1
S { x: a, y: b } => {}匹配结构体与 any(!),并且绑定 s.xas.yb
S { x, y } => {}同上,但分别地把 s.xs.y 的简写 xy 作为绑定
S { .. } => {}将 struct 与任何值匹配
D => {}如果 Duse 中,匹配枚举变体到 E::D
D => {}如果 D 不在 use 中,匹配任何东西绑定到 D,也可能是 E::D 的一种错误形式
_ => {}匹配任何内容(所有其余部分)的完全通配符。
0 | 1 => {}模式选择,or-patterns
E::A | E::Z => {}同上,但是在枚举变体上
E::C {x} | E::D {x} => {}同上,但如果所有变体都有它,则绑定 x
Some(A | B) => {}同上,可以匹配深度嵌套的 or-patterns
(a, 0) => {}a0 匹配任何值的元组
[a, 0] => {}切片模式,为 a0 匹配任何值的数组
[1, ..] => {}子切片模式,匹配数组从 1 开始,任何其余的值
[1, .., 5] => {}匹配数组从 1 开始到 5 结束中的值
[1, x @ .., 5] => {}同上,但还会绑定 x 到代表中间的切片
[a, x @ .., b] => {}同上,但分别匹配任意的第一个和最后一个,绑定为 ab
1 .. 3 => {}范围模式,这里会匹配 12,部分不稳定
1 ..= 3 => {}包含范围模式,匹配 123
1 .. => {}开放的范围模式,匹配 1 和任何更大的数
x @ 1..=5 => {}绑定匹配到 x模式绑定,此处的 x 将是 12,... 或 5
Err(x @ Error {..}) => {}也可以用于嵌套,这里 x 绑定到 Error,特别是下面有用的 if 形式
S { x } if x > 10 => {}模式 匹配保护,条件也必须为真才能匹配

泛型和约束

  • 泛型结合类型构造函数,特征和函数为你的代码提供更大的灵活性
例子解释
S<T>带有类型参数的 泛型 类型(T 表示占位符名称)
S<T: R>打印简写的 trait bound 范式(R 必须是实际的特征)
T: R, P: SIndependent trait bounds(这里一个给 T,一个给 P
T: R, S编译报错,你可能希望像使用下面的用法 R + S
T: R + SCompound trait boundT 必须满足 RS
T: R + 'a同上,但是 T 必须满足 R,如果 T 有生命周期,必须比 'a 更久
T: ?Sized选择离开预定义的 trait bound,这里是 Sized
T: 'a类型的 lifetime bound,如果 T 存在引用,则必须比 'a 更久
T: 'static同上,特别是不意味着 T 将会 'static,仅可以会 'static
'b: 'a'b 的生命周期必须存活至少为 'a
S<const N: usize>Generic const bound,类型 S 的使用者可以提供常量值 N
S<10>在使用时,常量边界可以作为原始值提供
S<{5+5}>表达式必须放在大括号中
S<T> where T: R几乎和 S<T: R> 一样,但语法越长越易于阅读
S<T> where u8: R<T>允许创建生成其它类型的条件语句
S<T = R>Default parameters,更容易使用,但也很灵活
S<const N: u8 = 0>默认参数是常量,如 f(x: S) {} 中参数 N0
S<T = u8>默认参数是类型,如 f(x: S) {} 中参数 Tu8
S<'_>anonymous lifetime,如果可以明显的看出来,会要求编译器推断出来
S<_>anonymous type,如 let x: Vec<_> = iter.collect() 一样
S::<T>Turbofish 会调用类型消除歧义,如 f::<u32>() 一样
trait T<X> {}基于 X 之上的一个特征泛型,可以有多个 impl T for S(一对 X
trait T { type X; }定义 关联类型 X,仅 impl T for S 的一种可能
type X = R;impl T for S { type X = R; } 中设置关联类型
impl<T> S<T> {}为在 S<T> 中任意的 T 实现功能,T 是类型参数
impl S<T> {}完全为 S<T> 实现功能,T 是指定类型(如 S<u32>
fn f() -> impl TExistential types,返回一个 impl T 的未知调用者 S
fn f(x: &impl T)Trait bound impl traits, 类似于 fn f<S:T>(x: &S)
fn f(x: &dyn T)dynamic dispatch 的标记,f 不会被单态化(monomorphized
fn f() where Self: R;trait T {} 中,使 f 仅可访问 impl R 的已知类型
fn f() where Self: Sized;使用 Sized 可以从 dyn T trait object vtable 中选出 f,启用 trait object
fn f() where Self: R {}其它 R 有用的 w. dflt,方法(non dflt 无论怎样都需要被实现)

Higher-Ranked 例子

  • 实际的类型和特征,抽象于一些东西,通常是生命周期
例子解释
for<'a>higher-ranked bounds 的标记
trait T: for<'a> R<'a> {}任何 impl TS 都必须为了整个生命周期满足 R
fn(&'a u8)函数指针类型持有 fn 可调用的指定的生命周期
for<'a> fn(&'a u8)Higher-ranked type 持有 fn 可调用的任何上面的子类型
fn(&'_ u8)同上,自动展开到类型 for<'a> fn(&'a u8)
fn(&u8)同上,自动展开到类型 for<'a> fn(&'a u8)
dyn for<'a> Fn(&'a u8)Higher-ranked(trait-object)类型,像上面的 fn 一样
dyn Fn(&'_ u8)同上,自动展开到类型 dyn for<'a> Fn(&'a u8)
dyn Fn(&u8)同上,自动展开到类型 dyn for<'a> Fn(&'a u8)

* for<> 是类型的一种,下面展示了 impl T for for<'a> fn(&'a u8) 这样的写法

实现特征解释
impl<'a> T for fn(&'a u8) {}对于指针 fn,其中调用接受指定的 'a, imple trait T
impl T for for<'a> fn(&'a u8) {} 对于指针 fn,其中调用接受任何的 imple trait T
impl T for fn(&u8) {}同上,简写版

字符串和字符

  • Rust 有几种创建文本值的方法
例子解释
"..."字符串文本,UTF-8 将解释 \n 为换行符
r"..."原始字符串文本,UTF-8 不会解释 \n
r#"..."#原始字符串文本,UTF-8 也可以包含 "# 数量是可变的
b"..."字节字符串文本,构造 ASCII [u8] ,不是字符串
br"...", br#"..."#原始字节字符串文本,ASCII [u8],以上的组合
'🦀'字符文本,固定 4 字节的 unicode char
b'x'ASCII 字节文本

文档注释

例子解释
///外部文档注释,通常用于类型、特征、函数
//!内行文档注释,主要用于文件到文档模块的开头
//行注释,使用这些来记录代码流或内部结构
/*...*/块注释
/**...*/外部块文档注释
/*!...*/内部块文档注释

其它杂项

  • 这些符号不属于任何其他类别,但知道了还是很好
例子解释
!始终为空
_未命名的通配符变量绑定,例如 |x, _| {}
let _ = x;未命名的分配是不执行的,不会 move out x 或保留目标值
_x变量绑定明确标记为未使用
1_234_567用于可清晰分辨的数字分隔符
1_u8数字文本 的类型说明符(还可以是 i8u16 …)
0xBEEF, 0o777, 0b1001十六进制(0x)、八进制(0o)和二进(0b)的整数文本
r#foo用于版本兼容的原始标识符
x;语句终止符或表达式

常用运算符

Rust 支持大多数运算符(+*%===,...),包括重载。因为它们在 Rust 中的行为没有什么不同,所以就不在此处列出它们。