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

为何Rust中多次调用borrow_mut_two会触发可变借用编译错误?

问题描述

可单次正常运行的函数

我编写了一个从向量中借用两个独立元素的函数borrow_mut_two,单次调用时可以正常编译并运行:

fn borrow_mut_two<T>(v: &mut [T], i: usize, j: usize) -> (&mut T, &mut T) {
  assert!(i < j);
  let (left, right) = v.split_at_mut(j);
  (&mut left[i], &mut right[0])
}

fn test() {
  let mut v = vec![0, 1, 2, 3, 4, 5, 6, 7, 8];
  let i = 2;
  let j = 5;
  let (ref_a, ref_b) = borrow_mut_two(&mut v, i, j);

  *ref_a += 1;
  *ref_b += 5;

  assert_eq!(*ref_a, i + 1);
  assert_eq!(*ref_b, j + 5);
}

第二次调用编译失败

但在test函数中第二次调用borrow_mut_two时,代码无法通过编译:

fn test() {
  let mut v = vec![0, 1, 2, 3, 4, 5, 6, 7, 8];
  let i = 2;
  let j = 5;
  let (ref_a, ref_b) = borrow_mut_two(&mut v, i, j);

  // 新增的第二次调用
  let (other_ref_a, other_ref_b) = borrow_mut_two(&mut v, i, j);

  *ref_a += 1;
  *ref_b += 5;

  assert_eq!(*ref_a, i + 1);
  assert_eq!(*ref_b, j + 5);
}

编译器报错显示存在两次可变借用冲突:

229 |     let (ref_a, ref_b) = borrow_mut_two(&mut v, i, j);
    |                                         ------ first mutable borrow occurs here
230 |     let (other_ref_a, other_ref_b) = borrow_mut_two(&mut v, i, j);
    |                                                     ^^^^^^ second mutable borrow occurs here

猜想与疑问

我猜想这是因为Rust的生命周期省略规则自动为函数添加了生命周期参数,使得函数签名等价于:

fn borrow_mut_two<'a, T>(v: &'a mut [T], i: usize, j: usize) -> (&'a mut T, &'a mut T)

导致第一次调用的可变借用生命周期未结束时,第二次调用再次借用同一个向量,从而触发冲突。我想确认该猜想是否正确,同时疑问Rust的第二条生命周期省略规则是否适用于元组中的每个元素?


解答

猜想确认:完全正确

你的推断是对的。Rust的生命周期省略规则中,当函数只有一个输入生命周期参数时(这里就是&'a mut [T]的'a),所有返回的引用类型都会被自动赋予这个生命周期。因此borrow_mut_two返回的两个&mut T都会绑定到输入向量的生命周期'a。

第一次调用后,ref_a和ref_b会一直持有对v的可变借用直到它们被销毁(也就是test函数结束),这段时间内v处于被可变借用的状态,第二次调用尝试再次可变借用同一个向量,自然违反了Rust的“可变引用独占”规则,导致编译报错。

生命周期省略规则对元组的适用性

第二条生命周期省略规则完全适用于元组中的每个元素。规则核心是:只要函数只有一个输入生命周期参数,所有返回的引用(不管是单个引用,还是元组、结构体等复合类型内部的引用)都会共享这个输入生命周期。所以你的返回元组里的两个&mut T都会被推断为'a,这正是问题的核心原因。

解决方法

如果需要允许多次调用borrow_mut_two,最简单的方式是让第一次的借用提前结束。可以把第一次调用的操作放在一个代码块中,当代码块结束时,ref_a和ref_b会被销毁,对应的可变借用也会被释放:

fn test() {
  let mut v = vec![0, 1, 2, 3, 4, 5, 6, 7, 8];
  let i = 2;
  let j = 5;
  
  // 用代码块限制第一次借用的生命周期
  {
    let (ref_a, ref_b) = borrow_mut_two(&mut v, i, j);
    *ref_a += 1;
    *ref_b += 5;
    assert_eq!(*ref_a, i + 1);
    assert_eq!(*ref_b, j + 5);
  }

  // 第一次借用已释放,可以正常第二次调用
  let (other_ref_a, other_ref_b) = borrow_mut_two(&mut v, i, j);
  // 后续对other_ref_a和other_ref_b的操作
}

如果需要同时持有多组不重叠的元素借用,Rust的安全类型系统无法直接做到(因为编译器无法静态证明不同调用的索引不重叠),这种情况下可以考虑使用unsafe代码手动实现,但需要自己保证索引的安全性,避免出现数据竞争或悬垂引用。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 03:05:28