开启RefCell debug_refcell feature查看借用冲突位置

文章目录

本文解决两个问题:

  • 开启rust源码库中的feature
  • 开启debug_refcell feature的方法查看 borrow 借用冲突的位置

背景

使用 RefCell 来实现内部可变性是在 Rust 开发中常用的方式,但是当逻辑复杂起来,常常会有可变借用和不可变借用之间的冲突。在默认情况下,Rust只会报错最后冲突的位置,即第7行,而第6行不会在错误堆栈出现。如果逻辑复杂起来,b的位置不在这么明显的位置,根本无从查起。

分析

查看 borrow() 和 borrow_mut() 的源码,它们分别是调用了 try_borrow() 和 try_borrow_mut(),在 try_borrow() 和 try_borrow_mut() 里面,发现了这么一段代码:

rust 复制代码
 Some(b) => {
     #[cfg(feature = "debug_refcell")]
     {
         // `borrowed_at` is always the *first* active borrow
         if b.borrow.get() == 1 {
             self.borrowed_at.set(Some(crate::panic::Location::caller()));
         }
     }

     // SAFETY: `BorrowRef` ensures that there is only immutable access
     // to the value while borrowed.
     let value = unsafe { NonNull::new_unchecked(self.value.get()) };
     Ok(Ref { value, borrow: b })
 }
 None => Err(BorrowError {
     // If a borrow occurred, then we must already have an outstanding borrow,
     // so `borrowed_at` will be `Some`
     #[cfg(feature = "debug_refcell")]
     location: self.borrowed_at.get().unwrap(),
 }),

可以看到 RefCell 有 borrowed_at 这么一个字段,当是 Some 并且 b.borrow.get() 是1的时候,设置了 borrowed_at;当是 None 的时候,给 BorrowError 传了 borrowed_at这个参数。

我们可以合理推测,borrowed_at是第一个被借用的"变量"。来看看 RefCell 结构体定义的地方:

rust 复制代码
pub struct RefCell<T: ?Sized> {
    borrow: Cell<BorrowFlag>,
    // Stores the location of the earliest currently active borrow.
    // This gets updated whenever we go from having zero borrows
    // to having a single borrow. When a borrow occurs, this gets included
    // in the generated `BorrowError`/`BorrowMutError`
    #[cfg(feature = "debug_refcell")]
    borrowed_at: Cell<Option<&'static crate::panic::Location<'static>>>,
    value: UnsafeCell<T>,
}

注释解释的很清楚了,borrowed_at就是最早发生的借用,当借用冲突发生的时候,它可以在错误中生成。

那么就是用 borrowed_at 查看冲突的位置了,但是要用它就需要打开 debug_refcell feature,默认情况下显然没有开启(因为上面的错误信息并没有最早发生借用的位置),而直接在项目的 Cargo.toml 中添加 debug_refcell 是不行的,因为这只是在我们自己的项目中开启feature,rust库是不知道的。

接下来就是要解决怎么在 rust源码库中开启指定 feature。

解决方法

询问 ChatGPT,需要在cargo build的时候编译源码库,并为其指定feature:

bash 复制代码
cargo +nightly run --target=x86_64-pc-windows-msvc -Zbuild-std -Zbuild-std-features="core/debug_refcell"

这次报错信息出现了最早借用的位置,即第6行。

这种方式需要使用 nightly 版本的rust编译器

bash 复制代码
rustup toolchain install nightly
rustup component add rust-src --toolchain nightly-x86_64-pc-windows-msvc
# 验证 nightly 版本是否安装
# 不需要切换到 nightly,上面的命令指定了这次编译的编译器版本是 nightly
rustup toolchain list 

如果你的项目环境是 no_std,需要去指定编译的库(排除std)

bash 复制代码
cargo +nightly build --release --target thumbv8m.main-none-eabihf -Zbuild-std=core,alloc -Zbuild-std-features="core/debug_refcell"
相关推荐
geovindu13 小时前
rust: Abstract Factory pattern
开发语言·后端·设计模式·rust·抽象工厂模式
Hi2024021718 小时前
同一个 NMS 算子,两种语言实现:CUDA C 与 Rust 生成 LLVM IR 和 Vortex 汇编的差异
c语言·汇编·rust·gpgpu·vortex·cudac
滕州市燕猫虎计算机科技工作室个体工商户18 小时前
《Rust程序设计》学习笔记一
开发语言·rust·学习笔记·rust程序设计
小道士写程序21 小时前
Windows Rust 环境安装指导
开发语言·windows·rust
leikooo2 天前
用餐厅和盘子理解栈和堆
java·数据结构·链表·rust
@大迁世界3 天前
JEV RAG:更高效的 RAG 系统解决方案?
开发语言·rust
进击的雷神3 天前
OpenClaw 太重、启动太慢?我试了试这个 Rust 重写的轻量版 AI 助手网关
开发语言·人工智能·rust·zeroclaw
选择即世界3 天前
高性能的 SGLang Rust 运行时
rust