编程语言研究书库

调用、运算符与类型转换 — 表达式(下)

调用、运算符与类型转换 — 表达式(下)

这一章是语法手册式的收尾: 函数和方法怎么调、字段和切片怎么取、 运算符里哪些是 C 的老样子、哪些悄悄改了规则。没有大的理论, 但每一条「与 C 不同」背后都有一次踩坑史。

1. 顶层全景:从大到小

  调用:函数 gcd(a,b) / 方法 v.sort() / 类型关联函数 Vec::new()
      │
      ▼
  取值:字段 point.x · 元组字段 t.0 · 下标 v[i] · 切片 &v[a..b]
      │
      ▼
  运算与转换:算术/位/比较/逻辑 · 赋值与复合赋值 · as · deref 强转

图说:. 号和 [] 号都会自动解引用——「指到第几层」不用你数。

本章的主线例子是快排的两个递归(函数自己调自己)切片:quicksort(&mut slice[..pivot_index]) 和 quicksort(&mut slice[pivot_index + 1..])——区间切片、可变借用加递归,三个语法点一步全走完。

2. 三种调用形态与一个语法怪癖

  • 函数调用:gcd(1302, 462)。
  • 方法调用:player.location()。. 号照例自动解引用:player 是 Player、&Player、 Box<Player> 还是 Rc<Player>,.location() 都能调——方法按值收、按引用收,匹配自动完成1。
  • 类型关联函数:Vec::new()——不是挂在一个值上,而是挂在类型上(对标 OOP 的静态方法)2。 链式调用随便串(第一章的 .bind(...).expect(...).run().expect(...))。

turbofish:在表达式里给泛型函数显式喂类型参数,不能写 Vec<T> 的尖括号—— 因为表达式里 < 是小于号,Vec<i32>::with_capacity(1000) 会被当成一链比较。 解法是加一对尖括号前导 :::Vec::<i32>::with_capacity(1000)、(0..n).collect::<Vec<i32>>()。 这个记号 ::<...> 在社区里的昵称就是 turbofish(涡轮鱼)3。 多数时候能省:返回类型或变量类型会兜住推断,能省就省是良好风格4。

链式调用为什么能链

第 1 章 web 服务器里那一长串 .bind(...).expect(...).run().expect(...) 能成立, 是因为这些方法的返回值又是带方法的对象,接着调即可。它不是语法特性,只是 「返回值设计」的副产品——配合「块/表达式的值」的规则,每个 .xxx() 既做事又交出下一个对象5。

字段、下标与 lvalue

能出现在赋值号左边的叫 lvalue(C 的老术语),Rust 里有三类:

game.black_pawns = 0x00ff0000_00000000_u64;   // 结构体字段
coords.1 = 0;                                  // 元组字段(按编号)
pieces[2] = Some(Piece::new(Black, Knight, coords));  // 数组/切片/向量元素

左边是引用或智能指针时自动解引用,和赋值无关的地方一样;当然,容器本身必须是 mut 的, 否则连字段都改不了。切片取值与赋值同一套下标:&game_moves[midpoint..end] 借一段, v[a..b] 的形态共六种,边界照旧检查6。

3. 字段、下标、切片区间

结构体字段 point.x、元组字段 pair.0、数组/切片/向量元素 pieces[i]—— 左边是引用或智能指针时自动解引用;这三类表达式可以出现在赋值号左边(此时要求容器本身是 mut 的)7。

区间切片:&v[a..b] 借出从下标 a 到 b(不含 b)的一段;两端都可以省略。 .. 按两端有无产出四种类型(RangeFull、RangeFrom、RangeTo、Range), ..= 再产出两种闭区间类型;只有带起点的区间能拿来 for 循环(循环得有个起点), 但六种形态在切片里全有用8。

回到主线例子,原书用快排展示切片在递归里的样子:

fn quicksort<T: Ord>(slice: &mut [T]) {
    if slice.len() <= 1 { return; }
    let pivot_index = partition(slice);
    quicksort(&mut slice[.. pivot_index]);        // 前一半:起点省略
    quicksort(&mut slice[pivot_index + 1 ..]);    // 后一半:终点省略
}

一次调用借出前半的可变切片,递归排完;再借出后半——两个借用前后错开、从不重叠, 所以每次都安全。泛型参数 <T: Ord>(只对可排序的类型)这里先当背景,细节在未放出的章节9。

区间类型速查

.. 按两端有无产出四种类型;..= 再补两种闭区间。只有带起点的两种能 for 循环 (循环总得从哪开始),六种形态在切片里全有用10:

写法类型含起点含终点能 for
..RangeFull——否(切片专用)
a..RangeFrom是到尾是
..bRangeTo从头否否
a..bRange是否是
..=bRangeToInclusive从头是否
a..=bRangeInclusive是是是

「含不含终点」的记忆口诀:两点的 .. 不含(0..3 是 0、1、2), 带等号的 ..= 含(0..=3 是 0、1、2、3)。切片里省略端点就默认顶到数据的头或尾。

链式调用的完整样本

第 1 章服务器的那段链值得再看一眼——它把「每步返回值都是下一个方法的接收者」 用到了实处11:

server
    .bind("127.0.0.1:3000").expect("error binding server to address")
    .run().expect("error running server");

bind 返回绑定好的 server,run 在它身上启动;两个 expect 各管一步的错误。 链式不是炫技,是「中间结果没有名字、也不需要名字」的诚实写法。

4. 算术、位、比较:和 C 分手的细节

大体是熟面孔,不同处才是考点:

  • 整数除法向零取整;整数除以零在 release 也 panic。 不想 panic 用 a.checked_div(b),返回 Option(除零时是 None)12。
  • % 取余对浮点也有效:1234.567 % 10.0 ≈ 4.567;结果符号跟左操作数13。
  • 没有一元 +;一元 - 不支持无符号数(-100u32 报错)14。
  • 位取反用 ! 不是 ~:所以 !n 不能当「n 是零」用,得写 n == 015。
  • 位移:有符号数恒符号扩展,无符号数恒零扩展——因为有明确的unsigned类型, Java 那个 >>>(强制零扩展)在 Rust 里没必要存在16。
  • 位运算优先级高于比较:x & BIT != 0 是 (x & BIT) != 0——「正如你本意」; C 里是 x & (BIT != 0),测的是错误的那个比特17。
  • 比较的两个操作数必须同类型;&&、|| 短路,操作数必须 bool18。
  • 没有 ++/--19。

赋值:= 给 mut 变量(及其字段/元素)赋值;非 Copy 类型赋值即移动(第 3 章)。 复合赋值(+= 等)齐;不支持链式赋值(a = b = 3 不行),不支持逗号表达式。 原书的解释很 Rust:赋值本来就罕见(变量默认不可变),这些捷径用不上20。

一元 *:什么时候真要写

Rust 的 . 号替你解引用,所以 * 的戏份比 C 少,但有一幕非它不可: 你要的是整个值,不是某个字段或方法。

let padovan: Vec<u64> = compute_padovan_sequence(n);
for elem in &padovan {
    draw_triangle(turtle, *elem);   // draw_triangle 要 u64,elem 是 &u64
}

elem 的类型是 &u64,函数要 u64——*elem 把「整个所指值」取出来21。 取字段(elem.x)和调方法(elem.len())有 . 号代劳,取整个值只能自己动手。

位运算与 C 的分手清单

把 Rust 从 C 的位运算习惯里带出来的差异收拢成一张表22:

主题CRust
按位取反~n!n
!n 的额外含义可当「n 为假」用只能作用于 bool——想判断整数零,写 n == 0
位移扩展有符号行为实现相关;Java 要 >>> 补零有符号恒符号扩展,无符号恒零扩展,无需第三运算符
x & BIT != 0解析成 x & (BIT != 0),测错比特位运算优先于比较,解析成 (x & BIT) != 0
% 取余整数专属浮点也行,符号跟左操作数

最后一条的例子:1234.567 % 10.0 约等于 4.56723。

赋值与复合赋值

= 给 mut 变量、字段、元素赋值;非 Copy 类型赋值即移动,目标原来的值先被丢弃 (第 3 章)。复合赋值(+= 等)是一组完整操作:total += item.price 就是 total = total + item.price24。

三个「没有」:没有链式赋值(a = b = 3 不行)、没有 ++/--、 没有逗号表达式。原书的解释透着 Rust 的脾气:变量默认不可变,赋值本来就罕见, 这些捷径没了就没了25。

5. as 转换:谁可以、谁禁止

转换的写法是 as。完整规则一张表26:

从 → 到规则
数值 ↔ 数值总是定义良好。变窄截断;有符号变宽符号扩展、无符号零扩展;浮点→整数向零取整,太大则夹到类型边界(1e6 as u8 是 255)
bool/char/类 C 枚举 → 整数可以
整数 → bool/char/枚举禁止。方向反过来需要运行时检查(如 u16 可能是代理区 0xd800,不是合法字符);要用就调 std::char::from_u32 拿 Option<char>
u8 → char唯一例外:0–255 全是合法码点
部分裸指针互转允许(需 unsafe 配合)

「禁止反向转换」的逻辑和溢出四族操作一脉相承:会静默出错的转换要么显式查、要么不许写27。

deref 强转(deref coercion):几种引用之间的转换自动发生、不用 as—— &String 自动转 &str,&Vec<i32> 自动转 &[i32],&Box<Chessboard> 自动转 &Chessboard。 它们适用于实现了 Deref trait 的类型,目的是让智能指针用起来尽量像底层值本身28。 自己写智能指针类型时也能实现 Deref(方法在未放出章节)。

6. 闭包:一句带过的大坑

let is_even = |x| x % 2 == 0;
is_even(14);   // true

闭包是轻量的「函数一样的值」:竖线里是参数,后面是表达式;参数类型和返回类型都能推断 (写返回类型时,体必须用大括号)29。调用语法和普通函数一样。 「Rust 闭包里最可喜的特性」——原书原话——这里卖了个关子,整章留给后面30。

deref 强转:智能指针「装成本体」的原理

&String → &str、&Vec<i32> → &[i32]、&Box<Chessboard> → &Chessboard, 这三种转换自动发生、不用 as,合称 deref 强转。背后的机制:这些类型都实现了 Deref 这个内建 trait,转换规则写在实现里31。

设计意图一句话:让智能指针用起来尽量像本体。Box<Chessboard> 基本上就是 一个 Chessboard——能传进任何收 &Chessboard 的函数,能直接调它的方法—— 靠的就是 deref 强转把「指针的外衣」悄悄脱了32。自己写智能指针类型时也能实现 Deref(方法在未放出章节)。

闭包写法的完整规则

let is_even = |x| x % 2 == 0;               // 参数类型、返回类型都推断
assert_eq!(is_even(14), true);              // 调用语法和普通函数一样

let is_even = |x: u64| -> bool { x % 2 == 0 };  // 显式写出:体必须用大括号

两条规矩:竖线里是参数;一旦显式声明返回类型,体必须是 { … } 块—— 这是语法健全性的小妥协,记住即可33。闭包的捕获、三种闭包 trait、 把闭包存进结构体,全是后面章节的内容,本章只交付「能读、能写、能调」。

7. 作者的判断与证据

  • 「位运算优先于比较」比 C 更有用是明确的判断,附带对 C 的吐槽(测错比特)17;
  • **「没有链式赋值、没有 ++/--,你不会想念它们」**建立在「赋值罕见」的前提上—— 这个前提来自「变量默认不可变」,链条完整,是论证不是口味20;
  • turbofish 的由来是语法冲突的客观结果(表达式里 < 已被小于号占用)3。

8. 边界与局限

  • as 里裸指针的转换只给了一句,unsafe 章节才展开;
  • 运算符重载(给自定义类型实现 +)本章没讲;
  • 闭包的捕获规则、三种闭包 trait(Fn/FnMut/FnOnce)整个缺席;
  • 区间类型在迭代器上下文((a..b).step_by(2))的用法留给后面章节。

9. 可带走的

主线例子一行版: quicksort(&mut slice[..p]) → 借前半可变切片递归 → 前后两个借用错开不重叠 → 合法。

  1. 三种调用:函数、方法(. 自动解引用)、类型关联函数(Vec::new);
  2. 表达式里给泛型喂类型用 ::<T>(turbofish),能推断就省略;
  3. 切片区间六种形态,递归分治的借力点;
  4. 整数除零 panic(任何构建);不想 panic 用 checked_div;
  5. % 可用于浮点;位取反是 !;
  6. 位运算优先级高于比较(Rust)——x & BIT != 0 就是你想的那个;
  7. 无 ++/--、无链式赋值、无一元 +;
  8. as:数值互转总合法;整数→char 禁止(代理区),u8→char 例外;反向都要检查;
  9. deref 强转:&String→&str、&Vec<T>→&[T] 自动发生;
  10. 闭包 |x| 表达式,类型可推断,大篇内容在后面。

10. 原文地图

主题原书章原文位置
三种调用形态Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:739(搜「location」) · text/15-ch05-chapter-5-expressions.txt:745(搜「type-associated」)
. 号自动解引用/借Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:743(搜「relaxes」)
turbofishChapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:760(搜「chained comparisons」) · text/15-ch05-chapter-5-expressions.txt:770(搜「turbofish」)
字段/下标/lvalueChapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:795(搜「lvalues」)
区间六种形态Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:811(搜「RangeFull」)
快排例子Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:827(搜「quicksort」)
整除/除零/checked_divChapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:865(搜「rounds toward zero」)
无一元 +Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:871(搜「leading +」)
取余与位取反Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:873(搜「modulus」) · text/15-ch05-chapter-5-expressions.txt:877(搜「bitwise NOT」)
位移扩展规则Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:884(搜「sign-extending」)
位运算优先级Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:886(搜「higher precedence」)
无 ++/--、无链式赋值Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:906(搜「increment and decrement」) · text/15-ch05-chapter-5-expressions.txt:904(搜「chaining」)
as 转换规则Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:910(搜「as keyword」) · text/15-ch05-chapter-5-expressions.txt:921(搜「rounds toward zero」) · text/15-ch05-chapter-5-expressions.txt:925(搜「surrogate」)
deref 强转Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:935(搜「auto-convert」) · text/15-ch05-chapter-5-expressions.txt:941(搜「deref coercions」)
一元 * 与整个值Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:850(搜「underlying values」)
位运算与 C 的分手清单Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:877(搜「bitwise NOT」)
deref 强转Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:935(搜「auto-convert」)
闭包写法规则Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:947(搜「vertical bars」)
链式调用与 lvalueChapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:751(搜「chained」) · text/15-ch05-chapter-5-expressions.txt:795(搜「lvalues」)
区间类型速查Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:811(搜「RangeFull」)
闭包入门Chapter 5. Expressionstext/15-ch05-chapter-5-expressions.txt:947(搜「lightweight」)

Footnotes

  1. 出处:「Chapter 5. Expressions」第 743 段(text/15-ch05-chapter-5-expressions.txt:743,搜「relaxes」)。 ↩

  2. 出处:「Chapter 5. Expressions」第 745–749 段(text/15-ch05-chapter-5-expressions.txt:745-749,搜「static methods」)。 ↩

  3. 出处:「Chapter 5. Expressions」第 758–770 段(text/15-ch05-chapter-5-expressions.txt:758-770,搜「chained comparisons」与「turbofish」)。 ↩ ↩2

  4. 出处:「Chapter 5. Expressions」第 772–778 段(text/15-ch05-chapter-5-expressions.txt:772-778,搜「good style」)。 ↩

  5. 出处:「Chapter 5. Expressions」第 751–756 段(text/15-ch05-chapter-5-expressions.txt:751-756,搜「chained」)。 ↩

  6. 出处:「Chapter 5. Expressions」第 780–801 段(text/15-ch05-chapter-5-expressions.txt:780-801,搜「lvalues」)。 ↩

  7. 出处:「Chapter 5. Expressions」第 780–801 段(text/15-ch05-chapter-5-expressions.txt:780-801,搜「lvalues」)。 ↩

  8. 出处:「Chapter 5. Expressions」第 809–825 段(text/15-ch05-chapter-5-expressions.txt:809-825,搜「RangeFull」)。 ↩

  9. 出处:「Chapter 5. Expressions」第 827–842 段(text/15-ch05-chapter-5-expressions.txt:827-842,搜「quicksort」)。 ↩

  10. 出处:「Chapter 5. Expressions」第 809–825 段(text/15-ch05-chapter-5-expressions.txt:809-825,搜「RangeFull」)。 ↩

  11. 出处:「Chapter 5. Expressions」第 751–756 段(text/15-ch05-chapter-5-expressions.txt:751-756,搜「chained」)。 ↩

  12. 出处:「Chapter 5. Expressions」第 863–865 段(text/15-ch05-chapter-5-expressions.txt:863-865,搜「rounds toward zero」)。 ↩

  13. 出处:「Chapter 5. Expressions」第 873–875 段(text/15-ch05-chapter-5-expressions.txt:873-875,搜「modulus」)。 ↩

  14. 出处:「Chapter 5. Expressions」第 867–871 段(text/15-ch05-chapter-5-expressions.txt:867-871,搜「leading +」)。 ↩

  15. 出处:「Chapter 5. Expressions」第 877–882 段(text/15-ch05-chapter-5-expressions.txt:877-882,搜「bitwise NOT」)。 ↩

  16. 出处:「Chapter 5. Expressions」第 884 段(text/15-ch05-chapter-5-expressions.txt:884,搜「sign-extending」)。 ↩

  17. 出处:「Chapter 5. Expressions」第 886 段(text/15-ch05-chapter-5-expressions.txt:886,搜「higher precedence」)。 ↩ ↩2

  18. 出处:「Chapter 5. Expressions」第 888–890 段(text/15-ch05-chapter-5-expressions.txt:888-890,搜「same type」)。 ↩

  19. 出处:「Chapter 5. Expressions」第 906 段(text/15-ch05-chapter-5-expressions.txt:906,搜「increment and decrement」)。 ↩

  20. 出处:「Chapter 5. Expressions」第 894–906 段(text/15-ch05-chapter-5-expressions.txt:894-906,搜「chaining」)。 ↩ ↩2

  21. 出处:「Chapter 5. Expressions」第 844–857 段(text/15-ch05-chapter-5-expressions.txt:844-857,搜「underlying values」)。 ↩

  22. 出处:「Chapter 5. Expressions」第 877–886 段(text/15-ch05-chapter-5-expressions.txt:877-886,搜「bitwise NOT」)。 ↩

  23. 出处:「Chapter 5. Expressions」第 873–875 段(text/15-ch05-chapter-5-expressions.txt:873-875,搜「approximately」)。 ↩

  24. 出处:「Chapter 5. Expressions」第 892–902 段(text/15-ch05-chapter-5-expressions.txt:892-902,搜「Compound assignment」)。 ↩

  25. 出处:「Chapter 5. Expressions」第 904–906 段(text/15-ch05-chapter-5-expressions.txt:904-906,搜「chaining」)。 ↩

  26. 出处:「Chapter 5. Expressions」第 910–929 段(text/15-ch05-chapter-5-expressions.txt:910-929,搜「as keyword」)。 ↩

  27. 出处:「Chapter 5. Expressions」第 921–925 段(text/15-ch05-chapter-5-expressions.txt:921-925,搜「surrogate」)。 ↩

  28. 出处:「Chapter 5. Expressions」第 931–943 段(text/15-ch05-chapter-5-expressions.txt:931-943,搜「deref coercions」)。 ↩

  29. 出处:「Chapter 5. Expressions」第 947–955 段(text/15-ch05-chapter-5-expressions.txt:947-955,搜「syntactic sanity」)。 ↩

  30. 出处:「Chapter 5. Expressions」第 961 段(text/15-ch05-chapter-5-expressions.txt:961,搜「most delightful」)。 ↩

  31. 出处:「Chapter 5. Expressions」第 931–941 段(text/15-ch05-chapter-5-expressions.txt:931-941,搜「Deref」)。 ↩

  32. 出处:「Chapter 5. Expressions」第 941 段(text/15-ch05-chapter-5-expressions.txt:941,搜「underlying value」)。 ↩

  33. 出处:「Chapter 5. Expressions」第 945–961 段(text/15-ch05-chapter-5-expressions.txt:945-961,搜「syntactic sanity」)。 ↩

On this page