{
  "about" : "学习随笔",
  "acceptsDonation" : false,
  "articles" : [
    {
      "articleNumber" : 2,
      "articleReference" : "373AC19D-2",
      "articleType" : 0,
      "attachments" : [

      ],
      "cids" : {

      },
      "content" : "示例代码:\n```rust\nfn main() {\n    let vec1 = vec![1, 2, 3];\n    let vec2 = vec![4, 5, 6];\n\n    // 对 vec1 的 `iter()` 举出 `&i32` 类型。\n    let mut iter = vec1.iter();\n    // 对 vec2 的 `into_iter()` 举出 `i32` 类型。\n    let mut into_iter = vec2.into_iter();\n\n    // 对迭代器举出的元素的引用是 `&&i32` 类型。解构成 `i32` 类型。\n    // 译注：注意 `find` 方法会把迭代器元素的引用传给闭包。迭代器元素自身\n    // 是 `&i32` 类型，所以传给闭包的是 `&&i32` 类型。\n    println!(\"Find 2 in vec1: {:?}\", iter     .find(|&&x| x == 2));\n    // 对迭代器举出的元素的引用是 `&i32` 类型。解构成 `i32` 类型。\n    println!(\"Find 2 in vec2: {:?}\", into_iter.find(| &x| x == 2));\n\n    let array1 = [1, 2, 3];\n    let array2 = [4, 5, 6];\n\n    // 对数组的 `iter()` 举出 `&i32`。\n    println!(\"Find 2 in array1: {:?}\", array1.iter()     .find(|&&x| x == 2));\n    // 对数组的 `into_iter()` 通常举出 `&i32``。\n    println!(\"Find 2 in array2: {:?}\", array2.into_iter().find(|&x| x == 2));\n}\n\n```\n\n疑问: 为什么`println!(\"Find 2 in vec1: {:?}\", iter     .find(|&&x| x == 2));`是&&?\n\n---\nfind方法的实现: \n```\npub trait Iterator {\n    // 被迭代的类型。\n    type Item;\n\n    // `find` 接受 `&mut self` 参数，表明函数的调用者可以被借用和修改，\n    // 但不会被消耗。\n    fn find<P>(&mut self, predicate: P) -> Option<Self::Item> where\n        // `FnMut` 表示被捕获的变量最多只能被修改，而不能被消耗。\n        // `&Self::Item` 指明了被捕获变量的类型（译注：是对迭代器元素的引用类型）\n        P: FnMut(&Self::Item) -> bool {}\n}\n\n```\n\n因为find接受的闭包参数是`FnMut(&Self::Item) -> bool`，也就是说，闭包收到的永远是迭代器元素的一个引用。\n\niter()返回的是item的引用-> `&i32`, 所以iter()->find",
      "contentRendered" : "<p>示例代码:</p>\n<pre><code class=\"language-rust\">fn main() {\n    let vec1 = vec![1, 2, 3];\n    let vec2 = vec![4, 5, 6];\n\n    // 对 vec1 的 `iter()` 举出 `&amp;i32` 类型。\n    let mut iter = vec1.iter();\n    // 对 vec2 的 `into_iter()` 举出 `i32` 类型。\n    let mut into_iter = vec2.into_iter();\n\n    // 对迭代器举出的元素的引用是 `&amp;&amp;i32` 类型。解构成 `i32` 类型。\n    // 译注：注意 `find` 方法会把迭代器元素的引用传给闭包。迭代器元素自身\n    // 是 `&amp;i32` 类型，所以传给闭包的是 `&amp;&amp;i32` 类型。\n    println!(&quot;Find 2 in vec1: {:?}&quot;, iter     .find(|&amp;&amp;x| x == 2));\n    // 对迭代器举出的元素的引用是 `&amp;i32` 类型。解构成 `i32` 类型。\n    println!(&quot;Find 2 in vec2: {:?}&quot;, into_iter.find(| &amp;x| x == 2));\n\n    let array1 = [1, 2, 3];\n    let array2 = [4, 5, 6];\n\n    // 对数组的 `iter()` 举出 `&amp;i32`。\n    println!(&quot;Find 2 in array1: {:?}&quot;, array1.iter()     .find(|&amp;&amp;x| x == 2));\n    // 对数组的 `into_iter()` 通常举出 `&amp;i32``。\n    println!(&quot;Find 2 in array2: {:?}&quot;, array2.into_iter().find(|&amp;x| x == 2));\n}\n\n</code></pre>\n<p>疑问: 为什么<code>println!(&quot;Find 2 in vec1: {:?}&quot;, iter     .find(|&amp;&amp;x| x == 2));</code>是&amp;&amp;?</p>\n<hr />\n<p>find方法的实现:</p>\n<pre><code>pub trait Iterator {\n    // 被迭代的类型。\n    type Item;\n\n    // `find` 接受 `&amp;mut self` 参数，表明函数的调用者可以被借用和修改，\n    // 但不会被消耗。\n    fn find&lt;P&gt;(&amp;mut self, predicate: P) -&gt; Option&lt;Self::Item&gt; where\n        // `FnMut` 表示被捕获的变量最多只能被修改，而不能被消耗。\n        // `&amp;Self::Item` 指明了被捕获变量的类型（译注：是对迭代器元素的引用类型）\n        P: FnMut(&amp;Self::Item) -&gt; bool {}\n}\n\n</code></pre>\n<p>因为find接受的闭包参数是<code>FnMut(&amp;Self::Item) -&gt; bool</code>，也就是说，闭包收到的永远是迭代器元素的一个引用。</p>\n<p>iter()返回的是item的引用-&gt; <code>&amp;i32</code>, 所以iter()-&gt;find</p>\n",
      "created" : 800333052.472001,
      "externalLink" : "",
      "hasAudio" : false,
      "hasVideo" : false,
      "id" : "4EB63666-509D-46BC-995D-061B85D94E4C",
      "link" : "/4EB63666-509D-46BC-995D-061B85D94E4C/",
      "modified" : 800349580.540459,
      "slug" : "",
      "tags" : {
        "rust学习笔记" : "Rust学习笔记"
      },
      "title" : "Find闭包中为什么会出现&&这种情况?"
    },
    {
      "articleNumber" : 1,
      "articleReference" : "373AC19D-1",
      "articleType" : 0,
      "attachments" : [

      ],
      "cids" : {

      },
      "content" : "\n### 示例代码: \n\n```rust\nfn main() {\n    // 获得一个 `i32` 类型的引用。`&` 表示取引用。\n    let reference = &4;\n\n    match reference {\n        // 如果用 `&val` 这个模式去匹配 `reference`，就相当于做这样的比较：\n        // `&i32`（译注：即 `reference` 的类型）\n        // `&val`（译注：即用于匹配的模式）\n        // ^ 我们看到，如果去掉匹配的 `&`，`i32` 应当赋给 `val`。\n        // 译注：因此可用 `val` 表示被 `reference` 引用的值 4。\n        &val => println!(\"Got a value via destructuring: {:?}\", val),\n    }\n\n    // 如果不想用 `&`，需要在匹配前解引用。\n    match *reference {\n        val => println!(\"Got a value via dereferencing: {:?}\", val),\n    }\n\n    // 如果一开始就不用引用，会怎样？ `reference` 是一个 `&` 类型，因为赋值语句\n    // 的右边已经是一个引用。但下面这个不是引用，因为右边不是。\n    let _not_a_reference = 3;\n\n    // Rust 对这种情况提供了 `ref`。它更改了赋值行为，从而可以对具体值创建引用。\n    // 下面这行将得到一个引用。\n    let ref _is_a_reference = 3;\n\n    // 相应地，定义两个非引用的变量，通过 `ref` 和 `ref mut` 仍可取得其引用。\n    let value = 5;\n    let mut mut_value = 6;\n\n    // 使用 `ref` 关键字来创建引用。\n    // 译注：下面的 r 是 `&i32` 类型，它像 `i32` 一样可以直接打印，因此用法上\n    // 似乎看不出什么区别。但读者可以把 `println!` 中的 `r` 改成 `*r`，仍然能\n    // 正常运行。前面例子中的 `println!` 里就不能是 `*val`，因为不能对整数解\n    // 引用。\n    match value {\n        ref r => println!(\"Got a reference to a value: {:?}\", r),\n    }\n\n    // 类似地使用 `ref mut`。\n    match mut_value {\n        ref mut m => {\n            // 已经获得了 `mut_value` 的引用，先要解引用，才能改变它的值。\n            *m += 10;\n            println!(\"We added 10. `mut_value`: {:?}\", m);\n        },\n    }\n}\n\n```\n\n### 运行结果:\n\n```\nGot a value via destructuring: 4\nGot a value via dereferencing: 4\nGot a reference to a value: 5\nWe added 10. `mut_value`: 16\n```\n\n---\n\n### 有个疑问没有搞明白: \n```\nlet value = 5;\n\nmatch value {\n    ref r => println!(\"Got a reference to a value: {:?}\", *r),\n}\n```\n可以运行, 但是\n```\nlet reference = &4;\n\nmatch reference {\n    &val => println!(\"Got a value via destructuring: {:?}\", *val),\n}\n```\n就运行不了, 会报错\n\n---\n\nRust 模式匹配中的 `&` 和 `ref` 意义相反：\n\n- `&` 在模式中：解构一个引用，提取出背后的值（所有权转移或复制）。\n- `ref` 在模式中：创建一个引用，绑定到匹配的值上。\n- let reference = &4 的reference取到的实际是&i32, 所以匹配&val就是取对应的值, 也就是4, 而4是具体的值, 使用*来解引用是不对的.\n\n---\n### 为什么这样写也能正常打印?: \n```\nlet value = 5;\n\nmatch value {\n    ref r => println!(\"Got a reference to a value: {:?}\", r),\n}\n```\n---\n\n#### 1. 两个版本的差异\n```rust\nlet value = 5;\n\nmatch value {\n    ref r => println!(\"Got a reference to a value: {:?}\", *r),   // 显式解引用\n}\n```\n\n```rust\nlet value = 5;\n\nmatch value {\n    ref r => println!(\"Got a reference to a value: {:?}\", r),    // 直接打印引用\n}\n```\n\n---\n\n#### 2. 为什么直接打印引用 `r` 也可以？\n\n关键在于 `println!` 的 `{:?}` 格式化器要求参数实现 `Debug` trait。  \n而标准库为 **所有引用类型**（`&T` 和 `&mut T`）都实现了 `Debug`，**前提是 `T` 本身实现了 `Debug`**。  \n\n具体来说标准库中有类似这样的实现（示意）：\n```rust\nimpl<T: Debug + ?Sized> Debug for &T {\n    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {\n        Debug::fmt(&**self, f)   // 递归地打印被引用的值\n    }\n}\n```\n因此当你打印一个 `&T` 时，输出的实际是 **它指向的 `T` 的 `Debug` 表示**，即自动解引用了一层（甚至多层）。\n\n- 如果 `value` 的类型是 `i32`（实现了 `Debug`），那么 `r` 的类型是 `&i32`，打印 `r` 会直接显示 `4`（假设 `value=4`）。\n- 这与显式写 `*r` 的效果在 **视觉输出上完全相同**，因为 `*r` 是 `i32`，打印 `i32` 也是显示 `4`。\n\n---\n\n#### 3. 本质区别：类型不同，但输出相同\n\n| 写法 | `r` 的类型 | `println!` 实际看到的参数类型 | 输出效果 |\n|------|-----------|----------------------------|----------|\n| `println!(\"...\", *r)` | `&i32` | `i32`（经解引用） | `4` |\n| `println!(\"...\", r)`  | `&i32` | `&i32` | `4`（因为 `Debug for &i32` 自动解引用） |\n\n虽然底层类型不同（一个是 `i32`，一个是 `&i32`），但因为 `Debug` 对引用的实现很“智能”，最终打印出来的字符串是一样的（除非 `T` 的 `Debug` 实现有特殊行为，例如打印地址而不是值——但普通类型不会）。\n\n---\n\n#### 4. 什么时候必须显式解引用？\n\n- 当需要 **值的所有权** 或 **值的实际类型** 进行运算时（例如 `*r + 1`）。\n- 当你使用的 trait 没有为引用实现，但为值实现了时（例如 `std::fmt::Display` 通常没有对引用自动实现，所以 `println!(\"{}\", r)` 可能报错，而 `println!(\"{}\", *r)` 可以）。\n- 当你需要精确控制解引用层数（例如遇到 `&&T` 时）。\n\n对于 `Debug` (`{:?}`)，由于标准库贴心地为所有引用实现了它，所以通常可以偷懒不写 `*`。\n\n---\n\n#### 5. 总结\n\n- `ref r` 绑定了一个引用 `&value`。\n- 直接打印 `r` 能工作，因为 `Debug for &T` 会自动打印出引用的值（类似于一次隐式解引用）。\n- `*r` 则是显式解引用得到 `T` 后再打印。\n- **功能上两者输出相同**，但前者少写一个 `*`，更简洁。\n\n所以两个写法（有 `*` 和无 `*`）都是正确的，区别只在于是否显式解引用。\n\n---\n\n### 为什么这样写也报错? \n```\nlet reference = &String::from(\"Hello\");\n\nmatch reference {\n    &val => println!(\"Got a value via destructuring: {:?}\", val),\n}\n\n```\n---\n> 原因在于 String 没有实现 Copy trait，而示例中的 i32 是 Copy 的。\n\n#### 1. 先看这个代码的编译错误:\n```\nerror[E0507]: cannot move out of `*reference` which is behind a shared reference\n --> src/main.rs:4:11\n  |\n4 |     &val => println!(\"...\", val),\n  |     ^^^\n  |     |\n  |     move occurs because `val` has type `String`, which does not implement the `Copy` trait\n  |     help: consider borrowing here: `&val`\n\n```\n#### 2. 模式 `&val` 做了什么？\n\n当你在 `match` 中对一个 `&T` 类型的值使用模式 `&val`：\n- 它会**解构这个引用**，把引用背后的 `T` 值**移动**（或复制）出来绑定到 `val`。\n- 对于 `&String`，`T` 就是 `String`（一个拥有堆上数据的类型）。\n- `String` 没有实现 `Copy`，所以 `val` 会**获取 `String` 的所有权**。\n\n但是这里有个严重问题：**`reference` 只是一个共享引用 `&String`**，它**并不拥有**这个 `String` 的所有权。所有权属于原本创建 `String` 的变量（如果存在）或者临时值。从共享引用中强行把所有权移走是**绝对禁止**的，因为这会让原所有者再也无法安全地释放内存。\n\n所以编译器报错：`cannot move out of borrowed content`。\n\n---\n\n#### 3. 为什么之前的 `&i32` 可以工作？\n\n```rust\nlet reference = &4;      // &i32\nmatch reference {\n    &val => println!(\"{}\", val),  // 可以运行\n}\n```\n- `i32` 实现了 `Copy`，因此 `&val` 模式不会“移动”所有权，而是**复制**出一个新的 `i32` 值给 `val`。\n- 复制一个 `i32` 只是复制 4 个字节，没有任何安全问题，也不影响原引用。\n\n这正是区别所在：**`Copy` 类型可以安全地从引用中取出值（复制），非 `Copy` 类型则不允许**。\n\n---\n\n#### 4. 如何修正非 `Copy` 类型的情况？\n\n\n#####  方案一：直接绑定引用（不析构）\n```rust\nmatch reference {\n    val => println!(\"Got a reference: {:?}\", val),  // val 是 &String\n}\n```\n`val` 的类型是 `&String`，没有移动所有权，只是把引用复制了一份（引用本身是 `Copy` 的）。\n\n##### 方案二：使用 `ref` 模式（显式创建引用）\n```rust\nmatch reference {\n    ref val => println!(\"Got a reference: {:?}\", val), // val 是 &&String（双重引用）\n}\n```\n`ref` 从匹配的值上创建一个引用，由于 `reference` 已经是 `&String`，`ref val` 会得到 `&&String`。打印时由于 `Debug` 的自动解引用，仍然会显示 `\"Hello\"`。\n\n##### 方案三：解引用后匹配值（需要所有权转移场景）\n如果你确实需要取得 `String` 的所有权，那必须确保你有权移动它——例如从原来的所有者变量直接匹配，而不是从引用：\n```rust\nlet s = String::from(\"Hello\");\nmatch s {        // 直接匹配 owned String\n    val => println!(\"{}\", val),   // val 获得所有权\n}\n```\n\n---\n\n#### 5. 总结\n\n> 为什么 `&val` 模式对 `&String` 报错，而对 `&i32` 不报错？\n\n- **`&val` 模式会尝试从引用中取出背后的值**。\n- 对于 `i32`（`Copy`），取出是**复制**，安全且允许。\n- 对于 `String`（非 `Copy`），取出是**移动所有权**，但共享引用 `&String` 不拥有所有权，因此被编译器禁止。\n\n修正方法通常是不用 `&val` 析构，而是直接匹配变量获得引用，或者小心处理所有权。\n",
      "contentRendered" : "<h3>示例代码:</h3>\n<pre><code class=\"language-rust\">fn main() {\n    // 获得一个 `i32` 类型的引用。`&amp;` 表示取引用。\n    let reference = &amp;4;\n\n    match reference {\n        // 如果用 `&amp;val` 这个模式去匹配 `reference`，就相当于做这样的比较：\n        // `&amp;i32`（译注：即 `reference` 的类型）\n        // `&amp;val`（译注：即用于匹配的模式）\n        // ^ 我们看到，如果去掉匹配的 `&amp;`，`i32` 应当赋给 `val`。\n        // 译注：因此可用 `val` 表示被 `reference` 引用的值 4。\n        &amp;val =&gt; println!(&quot;Got a value via destructuring: {:?}&quot;, val),\n    }\n\n    // 如果不想用 `&amp;`，需要在匹配前解引用。\n    match *reference {\n        val =&gt; println!(&quot;Got a value via dereferencing: {:?}&quot;, val),\n    }\n\n    // 如果一开始就不用引用，会怎样？ `reference` 是一个 `&amp;` 类型，因为赋值语句\n    // 的右边已经是一个引用。但下面这个不是引用，因为右边不是。\n    let _not_a_reference = 3;\n\n    // Rust 对这种情况提供了 `ref`。它更改了赋值行为，从而可以对具体值创建引用。\n    // 下面这行将得到一个引用。\n    let ref _is_a_reference = 3;\n\n    // 相应地，定义两个非引用的变量，通过 `ref` 和 `ref mut` 仍可取得其引用。\n    let value = 5;\n    let mut mut_value = 6;\n\n    // 使用 `ref` 关键字来创建引用。\n    // 译注：下面的 r 是 `&amp;i32` 类型，它像 `i32` 一样可以直接打印，因此用法上\n    // 似乎看不出什么区别。但读者可以把 `println!` 中的 `r` 改成 `*r`，仍然能\n    // 正常运行。前面例子中的 `println!` 里就不能是 `*val`，因为不能对整数解\n    // 引用。\n    match value {\n        ref r =&gt; println!(&quot;Got a reference to a value: {:?}&quot;, r),\n    }\n\n    // 类似地使用 `ref mut`。\n    match mut_value {\n        ref mut m =&gt; {\n            // 已经获得了 `mut_value` 的引用，先要解引用，才能改变它的值。\n            *m += 10;\n            println!(&quot;We added 10. `mut_value`: {:?}&quot;, m);\n        },\n    }\n}\n\n</code></pre>\n<h3>运行结果:</h3>\n<pre><code>Got a value via destructuring: 4\nGot a value via dereferencing: 4\nGot a reference to a value: 5\nWe added 10. `mut_value`: 16\n</code></pre>\n<hr />\n<h3>有个疑问没有搞明白:</h3>\n<pre><code>let value = 5;\n\nmatch value {\n    ref r =&gt; println!(&quot;Got a reference to a value: {:?}&quot;, *r),\n}\n</code></pre>\n<p>可以运行, 但是</p>\n<pre><code>let reference = &amp;4;\n\nmatch reference {\n    &amp;val =&gt; println!(&quot;Got a value via destructuring: {:?}&quot;, *val),\n}\n</code></pre>\n<p>就运行不了, 会报错</p>\n<hr />\n<p>Rust 模式匹配中的 <code>&amp;</code> 和 <code>ref</code> 意义相反：</p>\n<ul>\n<li><code>&amp;</code> 在模式中：解构一个引用，提取出背后的值（所有权转移或复制）。</li>\n<li><code>ref</code> 在模式中：创建一个引用，绑定到匹配的值上。</li>\n<li>let reference = &amp;4 的reference取到的实际是&amp;i32, 所以匹配&amp;val就是取对应的值, 也就是4, 而4是具体的值, 使用*来解引用是不对的.</li>\n</ul>\n<hr />\n<h3>为什么这样写也能正常打印?:</h3>\n<pre><code>let value = 5;\n\nmatch value {\n    ref r =&gt; println!(&quot;Got a reference to a value: {:?}&quot;, r),\n}\n</code></pre>\n<hr />\n<h4>1. 两个版本的差异</h4>\n<pre><code class=\"language-rust\">let value = 5;\n\nmatch value {\n    ref r =&gt; println!(&quot;Got a reference to a value: {:?}&quot;, *r),   // 显式解引用\n}\n</code></pre>\n<pre><code class=\"language-rust\">let value = 5;\n\nmatch value {\n    ref r =&gt; println!(&quot;Got a reference to a value: {:?}&quot;, r),    // 直接打印引用\n}\n</code></pre>\n<hr />\n<h4>2. 为什么直接打印引用 <code>r</code> 也可以？</h4>\n<p>关键在于 <code>println!</code> 的 <code>{:?}</code> 格式化器要求参数实现 <code>Debug</code> trait。<br />\n而标准库为 <strong>所有引用类型</strong>（<code>&amp;T</code> 和 <code>&amp;mut T</code>）都实现了 <code>Debug</code>，<strong>前提是 <code>T</code> 本身实现了 <code>Debug</code></strong>。</p>\n<p>具体来说标准库中有类似这样的实现（示意）：</p>\n<pre><code class=\"language-rust\">impl&lt;T: Debug + ?Sized&gt; Debug for &amp;T {\n    fn fmt(&amp;self, f: &amp;mut Formatter&lt;'_&gt;) -&gt; fmt::Result {\n        Debug::fmt(&amp;**self, f)   // 递归地打印被引用的值\n    }\n}\n</code></pre>\n<p>因此当你打印一个 <code>&amp;T</code> 时，输出的实际是 <strong>它指向的 <code>T</code> 的 <code>Debug</code> 表示</strong>，即自动解引用了一层（甚至多层）。</p>\n<ul>\n<li>如果 <code>value</code> 的类型是 <code>i32</code>（实现了 <code>Debug</code>），那么 <code>r</code> 的类型是 <code>&amp;i32</code>，打印 <code>r</code> 会直接显示 <code>4</code>（假设 <code>value=4</code>）。</li>\n<li>这与显式写 <code>*r</code> 的效果在 <strong>视觉输出上完全相同</strong>，因为 <code>*r</code> 是 <code>i32</code>，打印 <code>i32</code> 也是显示 <code>4</code>。</li>\n</ul>\n<hr />\n<h4>3. 本质区别：类型不同，但输出相同</h4>\n<table>\n<thead>\n<tr>\n<th>写法</th>\n<th><code>r</code> 的类型</th>\n<th><code>println!</code> 实际看到的参数类型</th>\n<th>输出效果</th>\n</tr>\n</thead>\n<tbody>\n<tr>\n<td><code>println!(&quot;...&quot;, *r)</code></td>\n<td><code>&amp;i32</code></td>\n<td><code>i32</code>（经解引用）</td>\n<td><code>4</code></td>\n</tr>\n<tr>\n<td><code>println!(&quot;...&quot;, r)</code></td>\n<td><code>&amp;i32</code></td>\n<td><code>&amp;i32</code></td>\n<td><code>4</code>（因为 <code>Debug for &amp;i32</code> 自动解引用）</td>\n</tr>\n</tbody>\n</table>\n<p>虽然底层类型不同（一个是 <code>i32</code>，一个是 <code>&amp;i32</code>），但因为 <code>Debug</code> 对引用的实现很“智能”，最终打印出来的字符串是一样的（除非 <code>T</code> 的 <code>Debug</code> 实现有特殊行为，例如打印地址而不是值——但普通类型不会）。</p>\n<hr />\n<h4>4. 什么时候必须显式解引用？</h4>\n<ul>\n<li>当需要 <strong>值的所有权</strong> 或 <strong>值的实际类型</strong> 进行运算时（例如 <code>*r + 1</code>）。</li>\n<li>当你使用的 trait 没有为引用实现，但为值实现了时（例如 <code>std::fmt::Display</code> 通常没有对引用自动实现，所以 <code>println!(&quot;{}&quot;, r)</code> 可能报错，而 <code>println!(&quot;{}&quot;, *r)</code> 可以）。</li>\n<li>当你需要精确控制解引用层数（例如遇到 <code>&amp;&amp;T</code> 时）。</li>\n</ul>\n<p>对于 <code>Debug</code> (<code>{:?}</code>)，由于标准库贴心地为所有引用实现了它，所以通常可以偷懒不写 <code>*</code>。</p>\n<hr />\n<h4>5. 总结</h4>\n<ul>\n<li><code>ref r</code> 绑定了一个引用 <code>&amp;value</code>。</li>\n<li>直接打印 <code>r</code> 能工作，因为 <code>Debug for &amp;T</code> 会自动打印出引用的值（类似于一次隐式解引用）。</li>\n<li><code>*r</code> 则是显式解引用得到 <code>T</code> 后再打印。</li>\n<li><strong>功能上两者输出相同</strong>，但前者少写一个 <code>*</code>，更简洁。</li>\n</ul>\n<p>所以两个写法（有 <code>*</code> 和无 <code>*</code>）都是正确的，区别只在于是否显式解引用。</p>\n<hr />\n<h3>为什么这样写也报错?</h3>\n<pre><code>let reference = &amp;String::from(&quot;Hello&quot;);\n\nmatch reference {\n    &amp;val =&gt; println!(&quot;Got a value via destructuring: {:?}&quot;, val),\n}\n\n</code></pre>\n<hr />\n<blockquote>\n<p>原因在于 String 没有实现 Copy trait，而示例中的 i32 是 Copy 的。</p>\n</blockquote>\n<h4>1. 先看这个代码的编译错误:</h4>\n<pre><code>error[E0507]: cannot move out of `*reference` which is behind a shared reference\n --&gt; src/main.rs:4:11\n  |\n4 |     &amp;val =&gt; println!(&quot;...&quot;, val),\n  |     ^^^\n  |     |\n  |     move occurs because `val` has type `String`, which does not implement the `Copy` trait\n  |     help: consider borrowing here: `&amp;val`\n\n</code></pre>\n<h4>2. 模式 <code>&amp;val</code> 做了什么？</h4>\n<p>当你在 <code>match</code> 中对一个 <code>&amp;T</code> 类型的值使用模式 <code>&amp;val</code>：</p>\n<ul>\n<li>它会<strong>解构这个引用</strong>，把引用背后的 <code>T</code> 值<strong>移动</strong>（或复制）出来绑定到 <code>val</code>。</li>\n<li>对于 <code>&amp;String</code>，<code>T</code> 就是 <code>String</code>（一个拥有堆上数据的类型）。</li>\n<li><code>String</code> 没有实现 <code>Copy</code>，所以 <code>val</code> 会<strong>获取 <code>String</code> 的所有权</strong>。</li>\n</ul>\n<p>但是这里有个严重问题：<strong><code>reference</code> 只是一个共享引用 <code>&amp;String</code></strong>，它<strong>并不拥有</strong>这个 <code>String</code> 的所有权。所有权属于原本创建 <code>String</code> 的变量（如果存在）或者临时值。从共享引用中强行把所有权移走是<strong>绝对禁止</strong>的，因为这会让原所有者再也无法安全地释放内存。</p>\n<p>所以编译器报错：<code>cannot move out of borrowed content</code>。</p>\n<hr />\n<h4>3. 为什么之前的 <code>&amp;i32</code> 可以工作？</h4>\n<pre><code class=\"language-rust\">let reference = &amp;4;      // &amp;i32\nmatch reference {\n    &amp;val =&gt; println!(&quot;{}&quot;, val),  // 可以运行\n}\n</code></pre>\n<ul>\n<li><code>i32</code> 实现了 <code>Copy</code>，因此 <code>&amp;val</code> 模式不会“移动”所有权，而是<strong>复制</strong>出一个新的 <code>i32</code> 值给 <code>val</code>。</li>\n<li>复制一个 <code>i32</code> 只是复制 4 个字节，没有任何安全问题，也不影响原引用。</li>\n</ul>\n<p>这正是区别所在：<strong><code>Copy</code> 类型可以安全地从引用中取出值（复制），非 <code>Copy</code> 类型则不允许</strong>。</p>\n<hr />\n<h4>4. 如何修正非 <code>Copy</code> 类型的情况？</h4>\n<h5>方案一：直接绑定引用（不析构）</h5>\n<pre><code class=\"language-rust\">match reference {\n    val =&gt; println!(&quot;Got a reference: {:?}&quot;, val),  // val 是 &amp;String\n}\n</code></pre>\n<p><code>val</code> 的类型是 <code>&amp;String</code>，没有移动所有权，只是把引用复制了一份（引用本身是 <code>Copy</code> 的）。</p>\n<h5>方案二：使用 <code>ref</code> 模式（显式创建引用）</h5>\n<pre><code class=\"language-rust\">match reference {\n    ref val =&gt; println!(&quot;Got a reference: {:?}&quot;, val), // val 是 &amp;&amp;String（双重引用）\n}\n</code></pre>\n<p><code>ref</code> 从匹配的值上创建一个引用，由于 <code>reference</code> 已经是 <code>&amp;String</code>，<code>ref val</code> 会得到 <code>&amp;&amp;String</code>。打印时由于 <code>Debug</code> 的自动解引用，仍然会显示 <code>&quot;Hello&quot;</code>。</p>\n<h5>方案三：解引用后匹配值（需要所有权转移场景）</h5>\n<p>如果你确实需要取得 <code>String</code> 的所有权，那必须确保你有权移动它——例如从原来的所有者变量直接匹配，而不是从引用：</p>\n<pre><code class=\"language-rust\">let s = String::from(&quot;Hello&quot;);\nmatch s {        // 直接匹配 owned String\n    val =&gt; println!(&quot;{}&quot;, val),   // val 获得所有权\n}\n</code></pre>\n<hr />\n<h4>5. 总结</h4>\n<blockquote>\n<p>为什么 <code>&amp;val</code> 模式对 <code>&amp;String</code> 报错，而对 <code>&amp;i32</code> 不报错？</p>\n</blockquote>\n<ul>\n<li><strong><code>&amp;val</code> 模式会尝试从引用中取出背后的值</strong>。</li>\n<li>对于 <code>i32</code>（<code>Copy</code>），取出是<strong>复制</strong>，安全且允许。</li>\n<li>对于 <code>String</code>（非 <code>Copy</code>），取出是<strong>移动所有权</strong>，但共享引用 <code>&amp;String</code> 不拥有所有权，因此被编译器禁止。</li>\n</ul>\n<p>修正方法通常是不用 <code>&amp;val</code> 析构，而是直接匹配变量获得引用，或者小心处理所有权。</p>\n",
      "created" : 800204338.579684,
      "externalLink" : "",
      "hasAudio" : false,
      "hasVideo" : false,
      "id" : "E9BB128C-4C1D-461C-A7B7-1BC27893AE95",
      "link" : "/E9BB128C-4C1D-461C-A7B7-1BC27893AE95/",
      "modified" : 800349590.850611,
      "slug" : "",
      "tags" : {
        "rust学习笔记" : "Rust学习笔记"
      },
      "title" : "指针和引用"
    }
  ],
  "created" : 800204104.733865,
  "id" : "373AC19D-B41C-463F-BDB0-9AEAB030203B",
  "ipns" : "k51qzi5uqu5di0bv3sgqbz95jtkpt4x9p5ddbmtuelcg9tdshf49na3mpyvhce",
  "juiceboxEnabled" : false,
  "name" : "Joe's Study Notes",
  "plausibleEnabled" : false,
  "podcastCategories" : {

  },
  "podcastExplicit" : false,
  "podcastLanguage" : "en",
  "tags" : {
    "rust学习笔记" : "Rust学习笔记"
  },
  "updated" : 800349590.859869
}