跳转至

移动实战 — 控制流、下标、Copy 与共享所有权

这一章回答四个「那具体怎么办」: 分支和循环里怎么移动?怎么从一个向量里取走一个元素? 整数凭什么不用移动那套规矩?需要一个值有多个主人时找谁? 上一章给了所有权和移动的定义,这一章全是边界上的实战。

1. 顶层全景:规则的三处松动

  所有权树太死 → 三处松动:
    ① 移动(本章前部):把所有权从一处过户到另一处——建/拆/重组树的唯一工具
    ② Copy 类型(本章中部):简单类型免除移动,按位复制
    ③ Rc/Arc(本章后部):受限的多主人,引用计数兜底
  (还有第四处「借用」,整章留给下一章)

图说:松动都有代价和限制,没有一处是白送的。

2. 移动与控制流

原则一句话:只要一个变量「有可能被搬走、且之后没Definitely拿到新值」,它就被视为未初始化1。 落到具体控制流:

分支:条件求值后变量还有值,两个分支就都能搬走它——反正只走一条路:

let x = vec![10, 20, 30];
if c {
    f(x);   // 这里搬走,ok
} else {
    g(x);   // 这里也搬走,ok
}
h(x);       // 错误:某条路径上 x 已被搬走

循环:循环里搬走是禁止的——第一轮搬走了,第二轮它就是未初始化。除非每轮都Definitely补个新值:

let mut x = vec![10, 20, 30];
while f() {
    g(x);     // 搬走
    x = h();  // 下一轮之前Definitely补新值——允许
}

这套「definite assignment(确定赋值)」式的分析不新鲜,Java 也有类似检查; Rust 选的是简单一侧:流敏感分析不猜循环条件的真假,假设任何条件都可能真也可能假2。

3. 主线例子:怎么从向量里取走一个元素

这是本章的主线例子。向量 v 里有五个字符串 "101" 到 "105",你想把 "103" 拿走:

let third = v[2];   // 错误:cannot move out of index of `Vec<String>`

为什么不行? 移动会让源变「未初始化」。若允许 v[2] 搬走,向量就得记住 「第 3 个格子现在是空的」——最一般的情形下,每个向量都得随身带一份 「哪些元素活着」的位图。书的原话:向量就该是向量,不是别的3。

编译器的报错信息直接给了出路(「consider borrowing here: &v[2]」)——多数时候你要的只是看, 那就借。真要走所有权,有三种正路,各留不同的向量状态:

// 1. 弹尾巴:拿走最后一个,向量变短
let fifth = v.pop().expect("vector empty!");   // 得 "105"

// 2. swap_remove:拿走指定下标的,把最后一个填进它的位置(乱序但快)
let second = v.swap_remove(1);                 // 得 "102"

// 3. replace:塞个替补进去,拿走原来的
let third = std::mem::replace(&mut v[2], "substitute".to_string());  // 得 "103"

// 此时 v == ["101", "104", "substitute"] ——始终满员,只是可能变短

三招的共同点:取走后向量保持「完全填充」的状态——所有权系统永远不用记哪个格子空了4。

更顺手的版本:把字段类型改成 Option<String>。 None 本来就是合法值,「取走」就变成「换成 None」:

let first_name = composers[0].name.take();   // 拿走 Some("Palestrina"),原地留 None

Option::take() 就是 replace(&mut x, None) 的专名——这个手法常用到值得一个专属方法5。

还有一招:整个向量给循环。 for mut s in v 把向量所有权交给循环机制, 逐元素搬进 s,循环体里可以改它再打印;向量中途的任何「半空状态」对外不可见, 因为向量已经不在你手里了6。

三招的完整代码与结果(原书逐行给了断言)20:

let mut v = Vec::new();
for i in 101..106 { v.push(i.to_string()); }

let fifth = v.pop().expect("vector empty!");        // 1: 弹尾
assert_eq!(fifth, "105");

let second = v.swap_remove(1);                       // 2: 尾填洞
assert_eq!(second, "102");

let third = std::mem::replace(&mut v[2], "substitute".to_string());  // 3: 塞替补
assert_eq!(third, "103");

assert_eq!(v, vec!["101", "104", "substitute"]);     // 向量始终满员

对照下三种取法的取舍:pop 只碰尾巴,O(1);swap_remove 快,但破坏了元素顺序 (最后一个元素搬进了被挖的洞);replace 保序,但你要先造一个替补值。 没有全能选项,因为「快速取走 + 保持满员 + 保序」在向量身上不可兼得—— 这正是「所有权系统不记空位」这条决定的直接后果21。

整个向量交给 for:消耗式遍历

书上的「自由、平等、博爱」例子把消耗式遍历演得最完整31:

let v = vec![
    "liberté".to_string(),
    "égalité".to_string(),
    "fraternité".to_string(),
];
for mut s in v {          // 向量本身被移进循环机制
    s.push('!');          // 每个 String 归 s 所有,可以改
    println!("{s}");      // 用完即弃
}

for … in v 把向量所有权交给循环的内部机制,逐元素搬进 s。 因为向量已经不在你手里,没有任何代码能观察到它「半空」的中间态—— 「取走一个、剩两个」这种不完整状态被所有权挡在视野之外,这正是 「不记空位」规定的另一面32。

4. Copy 类型:移动的豁免名单

移动的规矩对整数这类值是纯粹的负担。对比:

let num1: i32 = 36;
let num2 = num1;      // i32 没有堆资源,没有任何东西需要「唯一主人」
println!("{num1}");   // 完全没问题

i32 就是内存里的一组比特,不拥有堆资源;把比特拷过去,源和目标就彻底独立了, 「源作废」这条规矩在这里防不了任何危险,只添麻烦7。

于是有了 Copy 类型:赋值即按位复制,源保持可用。名单上的是: 全部机器整数和浮点、char、bool,以及由 Copy 类型组成的元组和定长数组8。

反向判据(经验法则):任何「值被丢弃时需要做点特别的事」的类型都不能 Copy—— Vec 要释放元素、File 要关句柄、MutexGuard 要解锁。按位复制会让「谁负责原来的资源」说不清9。 Box 也不是 Copy:它拥有堆上的被指物10。

自定义类型默认不是 Copy。 哪怕 struct Label { number: u32 } 这么纯粹,传参也会搬走—— 编译器错得很难看(borrow of moved value)。要豁免,自己声明:

#[derive(Copy, Clone)]
struct Label { number: u32 }

前提是所有字段都 Copy;有 String 字段就报错。为什么不自动给所有够格的类型上 Copy? 因为 Copy 是实现者的重大承诺:改成非 Copy 是破坏性的,用它的代码大量要改。 所以默认保守,加入自愿11。

和 C++ 的分野:C++ 允许你重载赋值运算符、定义拷贝/移动构造函数—— 于是 a = b 可能触发任意行为。Rust 不允许:每个移动都是逐字节的浅拷贝加源作废, 每个拷贝也一样,只是源保留。作者的立论原则:代价必须对程序员可见—— 基本操作必须简单,昂贵的操作(如 clone)必须显式12。

内存里发生了什么:一次对照

原书把 String 和 i32 的赋值并排画在一张图里(图 3-11),值得用语言重放22:

let string1 = "somnambulance".to_string();
let string2 = string1;   // 移动:string1 的三个机器字进 string2,string1 作废

let num1: i32 = 36;
let num2 = num1;         // 复制:36 这份比特进 num2,num1 原样可用

字符串那次:堆里那 13 个字母原地没动,动的只是栈上的指针、容量、长度; 源变量从此「不算数」,因为不能有两个主人都以为自己该释放那块缓冲。 整数那次:36 就是全部,拷贝一份就是两个完全独立的 36,源变量作废防不住任何危险—— 所以 Rust 干脆不许它作废,这就是 Copy 存在的全部理由23。

自定义类型的两次报错

默认非 Copy 这件事,新手会连撞两次墙。第一次,传参后想用:

struct Label { number: u32 }
let l = Label { number: 3 };
print(l);                    // l 被搬进 print,在里面被丢弃
println!("{}", l.number);    // 错误:borrow of moved value: `l`

Label 不过是个 u32 的包装,被搬走纯属误伤。第二次,试图 derive 救场但字段不够格:

#[derive(Copy, Clone)]
struct StringLabel { name: String }   // 错误:the trait `Copy` cannot be implemented

报错指名道姓:这个字段不是 Copy。两次墙,一次教你「默认是移动」,一次教你 「Copy 是资格,不是语法」24。

5. Rc 与 Arc:受限的共享所有权

单一主人覆盖不了所有场景:有时你就是希望值「用到它的所有人都用完了再死」。 这就是引用计数指针的用途。Rust 的两个版本:Rc<T> 和 Arc<T>, 唯一区别是 Arc 用原子操作更新计数,可跨线程;Rc 更快但禁止跨线程—— 不需要跨线程就别付 Arc 的开销13。

let s: Rc<String> = Rc::new("shirataki".to_string());
let t = s.clone();   // 不复制 String!只多一个指针,计数 +1
let u = s.clone();   // 计数 +1,共 3

Rc::new 在堆上分配值并附一个计数。克隆 Rc 不复制 T——只加计数。 三个 Rc 指向同一块内存(计数 + 值);平常的移动规则管的是 Rc 指针本身; 最后一个 Rc 死了,值跟着死14。String 的方法可以隔着 Rc 直接用(s.contains(...))。

代价:Rc 的值不可变。 想 s.push_str(" noodles")?编译器拒绝:cannot borrow data in an Rc as mutable15。 理由先记下结论:Rust 的内存与线程安全,依赖「没有任何值同时被共享又被修改」这一铁律—— Rc 的值按定义可能被共享,所以必须不可变。为什么这条铁律成立,是下一章的事16。

坑:循环引用会泄漏。 两个 Rc 互相指着,两边的计数都降不到零,两块内存永远不放—— 引用计数的经典死结,Rust 也不例外17。但有个天然的缓冲: 造环必须经过「让旧值改指新值」,而 Rc 的值不可变,所以正常手段造不出环; 能造环的突破口是「内部可变性」(还没讲的特性),用 Rc 配它才可能循环泄漏。 部分链接改用弱指针 Weak 也能拆环——书里说本书不展开,查标准库文档18。

Rc 的完整现场:从创建到最后一个死

走一遍书上的完整例子25:

use std::rc::Rc;

let s: Rc<String> = Rc::new("shirataki".to_string());  // 堆上:String + 计数 1
let t = s.clone();    // 不复制 String!计数 2
let u = s.clone();    // 计数 3

此刻内存里只有一份 String 和一个计数,三个 Rc 指针都指着它。 String 的方法照常隔着 Rc 用,仿佛指针不存在:

assert!(s.contains("shira"));
assert_eq!(t.find("taki"), Some(5));
println!("{u} are quite chewy, almost bouncy, but lack flavor");

平常的移动规则管的是 Rc 指针本身:把 u 传给函数,函数得到一个指针、计数加一, 函数返回,指针死、计数减一。当最后一个 Rc 死了,计数归零,String 跟着死—— 释放时机和单一主人一样确定,只是「最后一个人」由计数说了算26。

Rc 和 Arc 的唯一区别在计数的更新方式:Rc 用普通加减(快,但两个线程同时加减会算错), Arc 用原子操作(慢一点,线程安全)。所以跨线程共享必须 Arc,Rust 会在编译期 拦住把 Rc 塞进线程的尝试——不是运行时才炸27。

循环泄漏的形状与拆法

引用计数的老问题在 Rust 里形状不变:两个 Rc 互相指(书里配了图:每个对象的计数 都被对方托在 1 以上),程序永远等不到「最后一个」——内存泄漏28。

Rust 的缓冲是结构性的:Rc 的值不可变,而造环必须让旧值改指新值, 旧值不可变 ⇒ 正常手段造不出环。漏洞只在「内部可变性」(还没讲的特性)身上; 真造出来了,书上给的拆法是把环上的一条边换成弱指针 Weak—— 弱指针指着值但不托计数,环就断了。Weak 的细节本书不展开29。

与 C++ 的最后一对照:不能定制移动

C++ 里赋值是可以「做手脚」的:重载赋值运算符、写拷贝/移动构造函数, 于是 a = b 背后可能触发任意复杂的逻辑。Rust 把这扇门焊死了: 移动就是逐字节浅拷贝 + 源作废,每个类型、每次移动都一样,没有特例30。

代价和收益原书掂得很清楚。代价:有些 C++ 里很顺手的接口 Rust 给不了—— 普通-looking 的代码隐式调计数、推迟昂贵复制,这类戏法没地方演。 收益:基本操作的代价变得可读。赋值就是搬三个机器字,传参就是过户, 深复制必须喊出 clone 的名字——「代价必须对程序员可见」这条原则,移动语义是它的最大应用31。

这也是全书第一次出现的「Rust 式取舍」:不是这个好不好,而是可预测性和表达力选哪个。 移动语义、无隐式转换、无重载,都是同一枚硬币。

6. 作者的判断与证据

  • 「基本操作必须简单、昂贵操作必须显式」是原书明写的原则(costs should be apparent), 移动语义、无隐式转换、无重载赋值,全是这一条原则的产物12。
  • 「Rc 的坑罕见」是作者的经验之谈:「这样的情况很少见」——无数据支撑,但与 「不可变 + 需内部可变性才能成环」的机制自洽17。
  • 「对 C/C++ 老手,这笔交易不用想(no-brainer)」——作者立场,不是普适结论; 从 GC 语言过来的人适应成本不同19。

7. 边界与局限

  • 内部可变性(Cell/RefCell/Mutex)是 Rc 故事的另一半,本书还没放出,循环泄漏的完整图景缺一角;
  • Weak 指针只有一段指路;
  • take/replace/swap_remove 之外的集合取元素姿势(如 HashMap::remove)本章未列;
  • 计数指针的性能开销(原子操作在多核上的代价)只有一句「更快/更慢」的对比,无数值。

8. 可带走的

主线例子一行版: v[2] 不能直接搬走 → 要么 &v[2] 借,要么 pop/swap_remove/replace 取, 要么字段改 Option 用 take();目标都是让向量保持满员。

  1. 移动的控制流判据:变量可能已被搬走且没拿到新值 = 未初始化;分支两路各搬一次可以,循环里搬必须每轮换新;
  2. 不能 v[i] 取走元素,因为向量不会记「哪个格子空了」;三种取法:pop(弹尾)、swap_remove(尾填洞)、replace(塞替补);
  3. Option::take() = replace(x, None),「取走留洞」的标准写法;
  4. Copy 类型:按位复制、源保留;整数/浮点/char/bool 及 Copy 的组合;
  5. 需要特殊清理的类型(Vec/File/MutexGuard/Box)绝不能 Copy——「丢弃时要做事」是反向判据;
  6. 自定义类型默认移动;#[derive(Copy, Clone)] 自愿加入,且所有字段都得 Copy;
  7. Rust 禁止重载赋值:移动=浅拷贝+源作废,行为永远可预测;
  8. Rc/Arc 克隆只加计数不拷数据;最后一只指针死了值才死;Arc 原子计数、可跨线程;
  9. Rc 的值不可变——「不许同时共享又可变」这条铁律的第一块砖;
  10. 循环引用会泄漏,但正常造不出环;要造环得配内部可变性,拆环可用 Weak。

9. 原文地图

主题 原书章 原文位置
控制流与移动的一般原则 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:265(搜「definitely been given a new value」)
循环中移动的限制 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:279(搜「first iteration」)
不能从下标搬走 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:306(搜「uninitialized」) · text/13-ch03-chapter-3-ownership-and-moves.txt:308(搜「cannot move out of index」)
pop / swap_remove / replace Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:325(搜「Pop a value」) · text/13-ch03-chapter-3-ownership-and-moves.txt:331(搜「swap_remove」) · text/13-ch03-chapter-3-ownership-and-moves.txt:335(搜「replace」)
整个向量给循环 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:356(搜「dissects」)
take() 手法 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:378(搜「take method」)
Copy 的定义与名单 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:404(搜「Copy type」) · text/13-ch03-chapter-3-ownership-and-moves.txt:406(搜「machine integer」)
「丢弃时要做事 ⇒ 非 Copy」 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:410(搜「rule of thumb」)
derive(Copy, Clone) Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:437(搜「derive」)
实现者的承诺 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:456(搜「serious commitment」)
代价可见原则 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:460(搜「apparent to the programmer」)
Rc/Arc 分工 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:468(搜「atomic」)
克隆只加计数 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:479(搜「does not copy」)
Rc 值不可变 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:491(搜「immutable」) · text/13-ch03-chapter-3-ownership-and-moves.txt:497(搜「cannot borrow data」)
循环引用泄漏 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:505(搜「never be freed」)
三招取元素的完整代码 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:325(搜「Pop a value」)
String 与 i32 的内存对照 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:400(搜「pattern of bits」)
Rc 的完整现场 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:475(搜「shirataki」)
循环泄漏与 Weak Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:505(搜「never be freed」)
不能定制移动 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:458(搜「byte-for-byte」)
Weak 指针 Chapter 3. Ownership and Moves text/13-ch03-chapter-3-ownership-and-moves.txt:511(搜「weak pointers」)

  1. 出处:「Chapter 3. Ownership and Moves」第 265 段(text/13-ch03-chapter-3-ownership-and-moves.txt:265,搜「definitely been given a new value」)。 ↩

  2. 补充(不在本节原文):流程敏感分析取简单一侧的原则,见「Chapter 5. Expressions」第 695 段(text/15-ch05-chapter-5-expressions.txt:695,搜「simplicity」)。Java 的类似分析见同段(「definite assignment」)。 ↩

  3. 出处:「Chapter 3. Ownership and Moves」第 306 段(text/13-ch03-chapter-3-ownership-and-moves.txt:306,搜「extra information」)。原文:「那显然不是一种系统编程语言该有的行为;向量就该只是向量。」 ↩

  4. 出处:「Chapter 3. Ownership and Moves」第 341 段(text/13-ch03-chapter-3-ownership-and-moves.txt:341,搜「fully populated」)。 ↩

  5. 出处:「Chapter 3. Ownership and Moves」第 378 段(text/13-ch03-chapter-3-ownership-and-moves.txt:378,搜「take method」)。 ↩

  6. 出处:「Chapter 3. Ownership and Moves」第 351–356 段(text/13-ch03-chapter-3-ownership-and-moves.txt:351-356,搜「dissects」)。 ↩

  7. 出处:「Chapter 3. Ownership and Moves」第 393–402 段(text/13-ch03-chapter-3-ownership-and-moves.txt:393-402,搜「doesn’t own any heap resources」)。 ↩

  8. 出处:「Chapter 3. Ownership and Moves」第 404–406 段(text/13-ch03-chapter-3-ownership-and-moves.txt:404-406,搜「Copy types include」)。 ↩

  9. 出处:「Chapter 3. Ownership and Moves」第 410 段(text/13-ch03-chapter-3-ownership-and-moves.txt:410,搜「rule of thumb」)。 ↩

  10. 出处:「Chapter 3. Ownership and Moves」第 408 段(text/13-ch03-chapter-3-ownership-and-moves.txt:408,搜「Box<T>」)。 ↩

  11. 出处:「Chapter 3. Ownership and Moves」第 437–456 段(text/13-ch03-chapter-3-ownership-and-moves.txt:437-456,搜「serious commitment」)。 ↩

  12. 出处:「Chapter 3. Ownership and Moves」第 458–460 段(text/13-ch03-chapter-3-ownership-and-moves.txt:458-460,搜「apparent to the programmer」)。 ↩↩

  13. 出处:「Chapter 3. Ownership and Moves」第 468 段(text/13-ch03-chapter-3-ownership-and-moves.txt:468,搜「atomic」)。 ↩

  14. 出处:「Chapter 3. Ownership and Moves」第 475–483 段(text/13-ch03-chapter-3-ownership-and-moves.txt:475-483,搜「does not copy」)。 ↩

  15. 出处:「Chapter 3. Ownership and Moves」第 491–497 段(text/13-ch03-chapter-3-ownership-and-moves.txt:491-497,搜「cannot borrow data」)。 ↩

  16. 出处:「Chapter 3. Ownership and Moves」第 503 段(text/13-ch03-chapter-3-ownership-and-moves.txt:503,搜「simultaneously shared and mutable」)。 ↩

  17. 出处:「Chapter 3. Ownership and Moves」第 505 段(text/13-ch03-chapter-3-ownership-and-moves.txt:505,搜「never be freed」)。 ↩↩

  18. 出处:「Chapter 3. Ownership and Moves」第 509–511 段(text/13-ch03-chapter-3-ownership-and-moves.txt:509-511,搜「interior mutability」与「weak pointers」)。 ↩

  19. 出处:「Chapter 3. Ownership and Moves」第 37 段(text/13-ch03-chapter-3-ownership-and-moves.txt:37,搜「no-brainer」)。 ↩

  20. 出处:「Chapter 3. Ownership and Moves」第 319–341 段(text/13-ch03-chapter-3-ownership-and-moves.txt:319-341,搜「Pop a value」)。 ↩

  21. 出处:「Chapter 3. Ownership and Moves」第 341 段(text/13-ch03-chapter-3-ownership-and-moves.txt:341,搜「fully populated」)。 ↩

  22. 出处:「Chapter 3. Ownership and Moves」第 388–398 段(text/13-ch03-chapter-3-ownership-and-moves.txt:388-398,搜「pattern of bits」)。 ↩

  23. 出处:「Chapter 3. Ownership and Moves」第 400–402 段(text/13-ch03-chapter-3-ownership-and-moves.txt:400-402,搜「pointless」)。 ↩

  24. 出处:「Chapter 3. Ownership and Moves」第 412–455 段(text/13-ch03-chapter-3-ownership-and-moves.txt:412-455,搜「cannot be implemented」)。 ↩

  25. 出处:「Chapter 3. Ownership and Moves」第 470–489 段(text/13-ch03-chapter-3-ownership-and-moves.txt:470-489,搜「shirataki」)。 ↩

  26. 出处:「Chapter 3. Ownership and Moves」第 479–483 段(text/13-ch03-chapter-3-ownership-and-moves.txt:479-483,搜「last extant Rc」)。 ↩

  27. 出处:「Chapter 3. Ownership and Moves」第 468 段(text/13-ch03-chapter-3-ownership-and-moves.txt:468,搜「performance penalty」)。 ↩

  28. 出处:「Chapter 3. Ownership and Moves」第 505–509 段(text/13-ch03-chapter-3-ownership-and-moves.txt:505-509,搜「never be freed」)。 ↩

  29. 出处:「Chapter 3. Ownership and Moves」第 511 段(text/13-ch03-chapter-3-ownership-and-moves.txt:511,搜「Weak」)。 ↩

  30. 出处:「Chapter 3. Ownership and Moves」第 458 段(text/13-ch03-chapter-3-ownership-and-moves.txt:458,搜「byte-for-byte」)。 ↩

  31. 出处:「Chapter 3. Ownership and Moves」第 343–351 段(text/13-ch03-chapter-3-ownership-and-moves.txt:343-351,搜「liberté」)。 ↩↩

  32. 出处:「Chapter 3. Ownership and Moves」第 356 段(text/13-ch03-chapter-3-ownership-and-moves.txt:356,搜「partially emptied」)。 ↩