You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Rust中不同for循环变体的写法差异与报错原因咨询

Rust for循环模式匹配与所有权问题解答

要搞懂这些写法差异,先记住两个核心规则:

  1. for <模式> in <可迭代对象> 语法中,<模式>部分是解构匹配规则,不是变量类型声明。循环会把迭代器逐次产出的值,按你写的模式解构后绑定到对应变量上。
  2. for会自动对传入的可迭代对象调用into_iter(),不同类型调用后产出的迭代器元素类型不同,所有权规则也不同:
    • 传入不可变引用&Vec<T>:迭代元素类型为&T,不可变引用实现了Copy特性,迭代时不会拿走原变量的所有权
    • 传入可变引用&mut Vec<T>:迭代元素类型为&mut T,可变引用不实现Copy特性,直接传参会拿走原可变引用的所有权,迭代结束后原变量失效
    • 传入按值的Vec<T>:迭代元素类型为T,会逐个拿走容器内元素的所有权

分点解答你的疑问

1. loop1和loop2的写法差异

两者的唯一区别是循环头写的匹配模式不同:

  • loop2中写for i in list:list是&Vec<i32>,迭代产出的元素是&i32。这里i是无解构的变量绑定模式,直接把&i32绑定给i,所以i的类型就是&i32,必须用*i解引用才能拿到内部的i32值。如果直接传i给f64::from,就会报f64未实现From<&i32>的错误。
  • loop1中写for &i in list:这里的&i是引用解构模式,不是声明i为引用类型。迭代产出的&i32会被这个模式匹配,自动把引用指向的内部值绑定给i,因此i的类型直接是i32,不需要也不能再解引用——写*i会报错,因为i32是基础数值类型,不是指针,无法被解引用。

这个解构逻辑和let语句完全一致:写let &x = &10;时,x的类型就是i32,值为10。

你提到的loop3/4、loop5/6的差异和这个逻辑完全相同:loop4、loop7中i直接绑定迭代产出的引用,需要手动解引用;loop5、loop6用&i/&mut i的解构模式,直接拿到内部的i32值,无需解引用。

2. loop3_bad编译错误的根本原因

这个错误踩的是可变引用的所有权规则:
loop3_bad的参数是&mut Vec<i32>类型的可变引用,可变引用不支持Copy。你直接写for xxx in list时,会隐式调用list.into_iter(),这个调用会拿走list这个可变引用本身的所有权,循环结束后list变量已经失效,后续再调用list.len()就会触发“借用已移动的值”的错误。

为什么loop1、loop2没有这个问题?因为这两个函数的参数是不可变引用&Vec<i32>,不可变引用实现了Copy特性,调用into_iter()时会自动拷贝一份新的引用用于迭代,原list变量始终有效,循环结束后可以正常调用方法。

编译器给出的修复提示是正确的:只要不直接把list传给for循环,而是传入&list或者&mut list,就不会拿走原引用的所有权,避免这个错误。

3. &*list和&mut *list的作用

这是手动创建新引用、避免拿走原可变引用所有权的写法:
list本身是&mut Vec<i32>类型,先写*list解引用得到Vec<i32>类型的原列表,再写&/&mut取这个列表的引用,就得到了一个全新的引用。把这个新引用传给for循环迭代,不管迭代过程中怎么转移所有权,都不会影响原来的list变量,循环结束后可以正常调用list.len()。

实际开发中不需要写这么绕的语法,直接写for xxx in &list或者for xxx in &mut list就能达到完全一样的效果,可读性更好。

常用写法速查表

以后写循环可以直接对照这个表,不会再出类型错误:

函数参数类型循环写法循环变量i的类型变量使用方式
&Vec<i32>for i in list&i32需要*i解引用取值,不可修改元素
&Vec<i32>for &i in listi32直接使用i,不可修改元素
&mut Vec<i32>for i in &list&i32需要*i解引用取值,不可修改元素
&mut Vec<i32>for &i in &listi32直接使用i,不可修改元素
&mut Vec<i32>for i in &mut list&mut i32需要*i解引用取值,可修改元素
&mut Vec<i32>for &mut i in &mut listi32直接使用i,可修改元素

测试用完整代码

fn main() {
    // 原练习需要修改列表,因此声明为可变
    let mut list = vec![10, 14, 10, 12, 9, -2, 14, 10, 14];
    
    let ret1 = loop1(&list);
    let ret2 = loop2(&list);
    // 取消注释会触发编译错误
    // let ret3 = loop3_bad(&mut list);
    let ret4 = loop4(&mut list);
    let ret5 = loop5(&mut list);
    let ret6 = loop6(&mut list);
    let ret7 = loop7(&mut list);

    println!("loop1 ret={:?}", ret1);
    println!("loop2 ret={:?}", ret2);
    // println!("loop3 ret={:?}", ret3);
    println!("loop4 ret={:?}", ret4);
    println!("loop5 ret={:?}", ret5);
    println!("loop6 ret={:?}", ret6);
    println!("loop7 ret={:?}", ret7);
}

fn loop1(list: &Vec<i32>) -> Option<f64> {
    if list.is_empty() {
        None
    } else {
        let mut sum: f64 = 0.0;
        for &i in list {
            sum += f64::from(i);
        }
        Some(sum / list.len() as f64)
    }
}

fn loop2(list: &Vec<i32>) -> Option<f64> {
    if list.is_empty() {
        None
    } else {
        let mut sum: f64 = 0.0;
        for i in list {
            sum += f64::from(*i);
        }
        Some(sum / list.len() as f64)
    }
}

// 编译失败版本
// fn loop3_bad(list: &mut Vec<i32>) -> Option<f64> {
//     if list.is_empty() {
//         None
//     } else {
//         let mut sum: f64 = 0.0;
//         // 这里直接传list,会拿走可变引用的所有权
//         for &mut i in list {
//             sum += f64::from(i);
//         }
//         // 此处list已经失效,调用len()会报错
//         Some(sum / list.len() as f64)
//     }
// }

fn loop4(list: &mut Vec<i32>) -> Option<f64> {
    if list.is_empty() {
        None
    } else {
        let mut sum: f64 = 0.0;
        for i in &*list {
            sum += f64::from(*i);
        }
        Some(sum / list.len() as f64)
    }
}

fn loop5(list: &mut Vec<i32>) -> Option<f64> {
    if list.is_empty() {
        None
    } else {
        let mut sum: f64 = 0.0;
        for &i in &*list {
            sum += f64::from(i);
        }
        Some(sum / list.len() as f64)
    }
}

fn loop6(list: &mut Vec<i32>) -> Option<f64> {
    if list.is_empty() {
        None
    } else {
        let mut sum: f64 = 0.0;
        for &mut i in &mut *list {
            sum += f64::from(i);
        }
        Some(sum / list.len() as f64)
    }
}

fn loop7(list: &mut Vec<i32>) -> Option<f64> {
    if list.is_empty() {
        None
    } else {
        let mut sum: f64 = 0.0;
        for i in &mut *list {
            sum += f64::from(*i);
        }
        Some(sum / list.len() as f64)
    }
}

内容的提问来源于stack exchange,提问作者dhu

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 20:09:18