开启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"
相关推荐
uccs20 小时前
使用 rust 创建多线程 http-server
后端·rust
pumpkin845142 天前
Rust 的核心工具链
rust
SomeB1oody2 天前
【Rust自学】13.8. 迭代器 Pt.4:创建自定义迭代器
开发语言·后端·rust
半夏知半秋2 天前
rust学习-函数的定义与使用
服务器·开发语言·后端·学习·rust
SomeB1oody3 天前
【Rust自学】13.6. 迭代器 Pt.2:消耗和产生迭代器的方法
开发语言·后端·rust
Hello.Reader3 天前
Rust 数据类型详解
开发语言·后端·rust
gs801403 天前
2025年编程语言热度分析:Python领跑,Go与Rust崛起
python·golang·rust
老猿讲编程4 天前
详解Rust 中 String 和 str 的用途与区别
开发语言·后端·rust
rongjv4 天前
[rustGUI][iced]基于rust的GUI库iced(0.13)的部件学习(05):svg图片转为png格式(暨svg部件的使用)
rust·gui·iced
SomeB1oody4 天前
【Rust自学】13.5. 迭代器 Pt.1:迭代器的定义、iterator trait和next方法
开发语言·后端·rust