当前位置:网站首页>【Rust笔记】04-表达式
【Rust笔记】04-表达式
2022-06-22 05:45:00 【phial03】
04 - 表达式
4.1 - 表达式语言
- 在 C 语言中,表达式有值,而语句没有。
// 表达式
5 * (fahr - 32) / 9
// 语句
for (; begin != end; ++begin) {
if (*begin == target)
break;
}
- Rust 是表达式语言
(1) if 和 match 可以产生值
pixels[r * bounds.0 + c] =
match escapes(Complex {
re: point.0, im: point.1 }, 255) {
None => 0, // 如果匹配None,则值为0
Some(count) => 255 - count as u8 // 如果匹配Some(count),则值为u8类型的数值
};
(2) if 表达式可以用来初始化变量
let status =
if cpu.temperature <= MAX_TEMP {
// 条件成立,那么赋值为HttpStatus::Ok
HttpStatus::Ok
} else {
// 条件不成立,那么赋值为HttpStatus::ServerError
HttpStatus::ServerError // 服务器错误
};
(3) match 表达式可以作为参数传递给函数或宏
println!("Inside the vat, you see {}.",
match vat.contents {
Some(brain) => brain.desc(),
None => "nothing of interest"
}
);
(4) Rust 没有 C 语言的三元操作符(expr1 ? expr2 : expr3),完全可以通过上述实现代替。
4.2 - 块与分号
(1) Rust 的代码块,同样也是表达式,可以产生值:在代码块的最后一行省略分号,就会让这个块产生一个值
let display_name = match post.author() {
Some(author) => author.name(),
None => {
let network_info = post.get_network_metadata()?;
let ip = network_info.client_address();
ip.to_string() // 结尾没有分号
}
};
(2) 在 Rust 中,分号是有特殊意义的:
- 分号可以使得代码块既可以包含声明,又可以在末尾产生值
let msg = {
// let声明:分号是必须有的
let dandelion_control = puffball.open();
// 表达式 + 分号:方法调用,返回值被清除
dandelion_control.release_all_seeds(launch_codes);
// 表达式不带分号:方法被调用,返回值保存于msg中
dandelion_control.get_status()
};
技巧:当编译时,只要看到
expected type '() ,',首先看看是不是漏掉了分号。空语句,就是一个孤立的分号:
loop {
work();
paly();
; // 空语句
}
4.3 - 声明
- let 常用于声明变量,语法为:
let name: type = expr;
// 类型type和初始值expr是可选的,分号是必须的。
- let 声明可以只声明变量而不初始化它。之后可以通过赋值来初始化变量。这种情况常用于控制流结构中,可以在中间操作初始化变量:
let name; // 只声明了变量
if user.has_nickname() {
// 通过if条件语句,为变量赋值初始化
name = user.nickname();
} else {
name = generate_unique_name();
user.register(&name);
}
在初始化之前使用变量是错误的。
Rust 支持重新声明一个已有变量:
for line in file.lines() {
let line = line?;
}
上述代码等价于:
for line_result in file.lines() {
let line = line_result?; // ?用于检查可能失败的函数调用
}
// _result作为后缀,使得变量的类型是Result<String, io::Error>
// 而第二个变量line的类型是String
- 块里也可以包含特性项声明:可以在程序或模块的全局中出现的声明,比如
fn、struct或use。
4.4-if 与 match
4.4.1-if
- if 表达式
(1) 条件 condition 必须是一个 bool 型的表达式
(2) 条件 condition 可以不带圆括号。
if condition1 {
block1
} else if condition2 {
block2
} else {
block_n
}
- if 表达式中,所有的块 block 都必须产生相同类型的值
4.4.2-match
- match 表达式,等同于 C 中的 switch 语句
(1) 可以有多个表达式分支,但是只有 1 个会执行
match code {
0 => println!("OK"),
1 => println!("Wires Tangled"),
2 => println!("User Asleep"),
_ => println!("Unrecognized Error {}", code)
}
(2) _表示通配模式,可以匹配任何值,相当于 C 语言 switch 语句的 default 条件。
- match 表达式支持模式,常用于:
(1) 解包(unpack)元组
(2) 匹配结构体的个别字段
(3) 追索引用
(4) 借用一个值的某一部分
- match 表达式的通用语法形式:
match value {
pattern => expr,
...
}
(1) 如果 expr 是一个块,后面的逗号可以去掉
(2) 所有模式中必须至少有一个匹配项
(3) 所有分支都必须返回相同类型的值
4.4.3-if let
- if let 表达式:
if let pattern = expr {
block1
} else {
block2
}
(1) 实现了一种模式匹配,expr 要么匹配 pattern,然后运行 block1;
(2) 要么不匹配 pattern,而运行 block2。
(3) 常用于从 Option 或 Result 中取得数据:
if let Some(cookie) = request.session_cookie {
return restore_session(cookie);
}
if let Err(err) = present_cheesy_anti_robot_task() {
log_robot_attempt(err);
politely_accuse_user_of_being_a_robot();
} else {
session.mark_as_human();
}
- if let 表达式是对只有一个模式的 match 表达式的简写:
match expr {
pattern => {
block1 }
_ => {
block2 }
}
4.5 - 循环
- 循环在 Rust 中是表达式,但它不会产生有意义的值。
- 循环的值是基元 ()。
4.5.1-while
- 语法:
while codition {
block
}
- codition 必须是 bool 类型。
4.5.2-while let
- 语法:
while let pattern = expr {
block
}
- 与 if let 类似,在每次循环开始时,expr 的值要先匹配给定的 pattern,再运行后面的块;如果不匹配,那么会退出循环。
4.5.3-loop
- 语法:
loop {
block
}
- 用于编写无穷循环。
(1) block 会永远重复执行
(2) 直到遇到一个退出条件:brea、return 或 线程诧异
4.5.4-for
- 语法:
for pattern in collection {
block
}
先对 collection 表达式求值,然后该集合中每个值必须对 block 求值
支持的集合有很多种:
(1) .. 操作符:产生一个范围(range),即一个拥有两个字段(start 和 end)的简单结构体。
for i in 0..20 {
println!("{}", i);
}
// 0..20等价于std::ops::Range { start: 0, end: 20 }
// Range是可迭代类型
(2) 标准的集合,如数组和切片,都是可迭代的:每迭代一个值就用掉一个值
let strings: Vec<String> = error_message();
for s in strings {
println!("{}", s);
}
println!("{} error(s)", strings.len());
(3) 迭代对集合的引用:循环变量就是对集合中每一项的引用
for rs in &strings {
println!("String {:?} is at address {:p}.", *rs, rs);
}
// &strings的类型是&Vec<String>
// rs的类型是&String。
(4) 迭代 mut 引用:循环变量拿到的也是 mut 引用
for rs in &mut strings {
// rs的类型是&mut String
rs.push('\n'); // 给每个字符串添加一个换行符
}
4.5.5-break
- break 表达式用于退出闭合循环。
- 只能在循环中使用,match 表达式中用不到 break。
4.5.6-continue
- continue 表达式用于跳到循环的下一次迭代:
// 读取数据,每次读一行
for line in input_lines {
let trimmed = trim_comments_and_whitespace(line);
if trimmed.is_empty() {
// 跳到循环顶部,取得输入的下一行
continue;
}
...
}
- 在 for 循环中:
(1) continue 会前进到集合中的下一个值。
(2) 如果没有值了,则退出循环。 - 在 while 循环中:
(1) continue 会再次检查循环条件。
(2) 如果为假,则退出循环。
4.5.7 - 循环的生命周期
- 循环可以加上生命期标签:
'search: // 外部for循环的生命期标签
for room in apartment {
for spot in room.hiding_spots() {
if spot.contains(keys) {
println!("Your keys ar {} in the {}.", spot, room);
break 'search; // 此处表示break会退出外部循环,而不是内部循环。
}
}
}
- 生命期标签也可以与
continue一起使用
4.6-return 表达式
return 表达式可以退出当前函数,并向调用者返回一个值。
无值 return 表达式是 return() 函数的简写:
fn f() {
// 省略返回类型:默认为()
return; // 省略返回值:默认为()
}
return也可以结束当前的工作,等同于 break。?操作符:检查可能失败的函数调用。let output = File::create(filename)?;
等价于如下 match 表达式代码:
let output = match File::create(filename) {
Ok(f) => f, // 匹配后,f会保存在output中
Err(err) => return Err(err)
};
4.7-Rust 的循环特点
- Rust 编译器分析控制流的机制 —— 流敏感(flow-sensitive) 分析:
(1) 检查贯穿函数的每条路径,确保返回值为正确类型;
(2) 检查局部变量永远不会在未初始化时被使用;
(3) 对无法抵达的代码给出警告。
- 不正常结束的表达式,会被指定为特殊类型
!,它们不受其他类型需要尊总的规则约束。如std::process::exit()的函数签名中可以看到!:
fn exit(code: i32) -> !
! 意味着 exit() 永远不会返回,它是一个发散函数(divergent function)。
4.8 - 函数与方法调用
- 静态方法与非静态方法的区别:
(1) 静态方法通过类型调用,如 Vec::new();
(2) 非静态方法通过值调用,如 my_vec.len()。
- 方法可以链式调用:
Iron::new(router).http("localhost:3000").unwrap();
- 用于函数调用或方法调用的语法,不能用于泛型
Vec<T>:
(1) < 是一个小于操作符:
return Vec<i32>::with_capacity(1000); // 错误:需要进行链式比较
let ramp = (0..n).collection<Vec<i32>>(); // 错误,同上
(2) 针对此种情况,Rust 建议使用::<T>,而不是 <T>::
return Vec::<i32>::with_capacity(1000);
let ramp = (0..n).collection::<Vec<i32>>();
(3) ::<...> 称为极速鱼(turbofish)
(4) 也可以省略类型参数,让 Rust 编译器自行推断【推荐方法】:
return Vec::with_capacity(10);
let ramp: Vec<i32> = (0..n).collection();
4.9 - 字段与元素
.操作符左侧的值,如果是一个引用或智能指针类型,那么它就会跟方法调用一样自动解引用。[]方括号常用于访问数组、切片或向量中的元素:
pieces[i] // 数组元素
方括号左侧的值会自动解引用。
..范围操作符允许省略两侧的操作数。
.. // 表示全部范围;
a.. // 表示起始于a:{start: a}
.. b // 表示终止与b-1:{end: b}
a.. b // 表示范围>=a,<b:{start: a, end: b}
(1) 返回是 半开口(half-open) 的,包含起始值,不包含结尾值。
(2) 只有包含起始值的范围才是可迭代的,因为循环必须从某个地方开始。
- 采用经典分治法实现快速排序:
fn quicksort<T: ord>(slice: &mut [T]) {
if slice.len() <= 1 {
return;
}
// 将切片分成前、后两部分
let pivot_index = partition(slice);
// 递归对slice的前半部分,进行排序
quicksort(&mut slice[.. pivot_index]);
// 递归对slice的后半部分,进行排序
quicksort(&mut slice[pivot_index + 1 ..]);
}
4.10 - 引用操作符
- 取地址操作符:
&和&mut。 - 一元操作符
*:用于访问引用指向的值。只需读或写引用指向的整个值。 .点操作符会自动跟踪引用去访问字段或方法。
4.11 - 其他操作符
4.11.1 - 算术
| 算数操作符 | 说明 |
|---|---|
+ | 加 |
- | 减 |
* | 乘 |
/ | 除 |
% | 取模,或取余 |
4.11.2 - 位
| 位操作符 | 说明 |
|---|---|
| & | 位与 |
| | 位或 |
| ^ | 位异或 |
| ! | 位非 |
| << | 左移 |
>> | 右移 |
- 不能用 !n 表示 “n 是 0”,需要写成 n == 0。
- 位操作的优先级高于比较操作。
4.11.3 - 比较
| 比较操作符 | 说明 |
|---|---|
| == | 等于 |
| != | 不等于 |
| < | 小于 |
> | 大于 |
| <= | 小于等于 |
>= | 大于等于 |
4.11.4 - 逻辑
- 短路逻辑操作符的操作数,必须都是
bool类型。
| 短路逻辑操作符 | 说明 |
|---|---|
&& | 逻辑与 |
|| | 逻辑或 |
! | 逻辑非 |
4.11.5 - 赋值
=赋值操作符,用于把值赋给mut变量,以及他们的字段或元素。变量默认是不可修改的。
赋值会
转移非可赋值类型的值。Rust 支持复合赋值:
+=
-=
*=
/=
%=
<<=
>>=
&=
^=
|=
Rust 不支持链式赋值,如 C 语言中的
a=b=3。Rust 没有 C 语言中的递增操作符
++和递减操作符--
4.12 - 类型转换
- Rust 中,将一个值从一种类型,转换为另一种,需要做显示转换。
as操作符,实现类型转换。- 允许的类型转换:
(1) 数值可以从任何内置的数值类型转换为任意其他类型。
(2)bool、char或类 C 的enum类型的值,可以转换为任何整数类型。
(3) 有些涉及不安全指针类型的转换是允许的。 - 把 mut 引用转换为非 mut 引用,会直接转换,不需要显示进行。
- 以下是一些比较重要的自动转换,被称为解引用强制转换(deref corecion),适用于实现内置的
Deref特型的类型。
(1)&String类型的值会自动换换为&str类型。
(2)&Vec<i32>类型的值会自动转换为&[i32]。
(3)&Box<Chessboard>类型的值会自动转换为&Chessboard。
4.13 - 闭包
- Rust 的闭包,类似轻量级函数,通常由参数列表(在两条竖线中给出)和表达式组成:
let is_even = |x| x % 2 == 0;
Rust 会推断闭包的参数类型和返回类型。
如果明确指定了返回类型,那么闭包体必须是一个代码块(用 {} 括起来)。
let is_even = |x: u64| -> bool x % 2 == 0; // 错误
let is_even = |x: u64| -> bool {
x % 2 == 0}; // 可以
- 调用闭包,与调用函数的语法相同:
assert_eq!(is_even(14), true);
4.14 - 优先级
- 优先级列表,按照从高到低列出:
| 表达式类型 | 举例 |
|---|---|
| 数组字面量 | [1, 2, 3] |
| 重复的数组字面量 | [0; 50] |
| 元组 | (6, “crullers”) |
| 分组 | (2 + 2) |
| 块 | {f(); g()} |
| 控制流表达式 | if ok {f();} |
| 宏调用 | println!(“ok”); |
| 路径 | std::f64::consts::PI |
| 结构体字面量 | Point {x: 0, y: 0} |
| 元组字段存取 | pair.0 |
| 结构体字段存取 | point.x |
| 方法调用 | point.translate(50, 50) |
| 函数调用 | stdin() |
| 索引 | arr[0] |
| 错误检查 | create_dir(“tmp”)? |
| 逻辑 / 按位非 | !ok |
| 取反 | -num |
| 解引用 | *ptr |
| 借用 | &val |
| 类型转换 | x as u32 |
| 乘 | n * 2 |
| 除 | n / 2 |
| 取余(取模) | n % 2 |
| 加 | n + 2 |
| 减 | n - 2 |
| 左移 | n << 1 |
| 右移 | n >> 1 |
| 按位与 | n & 1 |
| 按位异或 | n ^ |
| 按位或 | `n |
| 小于 | n < 1 |
| 小于等于 | n <= 1 |
| 大于 | n > 1 |
| 大于等于 | n >= 1 |
| 等于 | n == 1 |
| 不等于 | n != 1 |
| 逻辑与 | x.ok && y.ok |
| 逻辑或 | `x.ok |
| 范围 | start..stop |
| 赋值 | x = val |
| 复合赋值 | x += 1 |
| 闭包 | ` |
所有上述操作符,在链式操作时,都具有左关联性。
比较操作符、赋值操作符和范围操作符
..不能执行链式操作
详见《Rust 程序设计》(吉姆 - 布兰迪、贾森 - 奥伦多夫著,李松峰译)第六章
链接地址
边栏推荐
- D3D10 截图功能 保存Texture到本地
- TCP连接细节问题
- Vulkan 预旋转处理设备方向
- Unity encrypts ASE game data
- 401-字符串(344. 反转字符串、541. 反转字符串II、题目:剑指Offer 05.替换空格、151. 颠倒字符串中的单词)
- Machine learning note 8: octave for handwritten digit recognition based on Neural Network
- D3D10 screenshot function saves texture to local
- Signal output library
- I2C interface
- 自控原理之系统辨识
猜你喜欢

生信可视化(part3)--小提琴图

单细胞论文记录(part12)--Unsupervised Spatial Embedded Deep Representation of Spatial Transcriptomics

D3D learning notes (1) - Introduction to the use conditions of autodraw at so stage

单精度,双精度和精度(转载)

D3D10 screenshot function saves texture to local

Use of idea plug-in EASYCODE

生信可视化(part4)--相关性图

Frame profiling

EPP (enhanced parallel port)

Case analysis of terminal data leakage prevention
随机推荐
PID notes
Write optimized DSP code for cortex-m4
BinaryFormatter saving and loading game data for unity
Improve your game‘s performance
MFC tab control add Icon
Unity development - scene asynchronous loading
性能优化 之 3D资产优化及顶点数据管理
DOS bat syntax record I
EMC的解决
Machine learning note 8: octave for handwritten digit recognition based on Neural Network
MFC Tab 控件添加 icon 图标
PIR控制器调节器并网逆变器电流谐波抑制策略
Error: note: module requires go 1.17
Bat common batch script record
Machine learning Note 6: number recognition of multiple classification problems in logistic regression
牛的学术圈 II(春季每日一题 49)
C指針的理解
生信可视化(part4)--相关性图
idea插件EasyCode的使用
Linear regression: least squares, Tellson estimation, RANSAC