开启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"
相关推荐
微小冷4 小时前
Rust异步编程详解
开发语言·rust·async·await·异步编程·tokio
鸿乃江边鸟4 小时前
Spark Datafusion Comet 向量化Rust Native--CometShuffleExchangeExec怎么控制读写
大数据·rust·spark·native
明飞19871 天前
tauri
rust
咚为1 天前
Rust tokio:Task ≠ Thread:Tokio 调度模型中的“假并发”与真实代价
开发语言·后端·rust
天天进步20151 天前
Motia性能进阶与未来:从现有源码推测 Rust 重构之路
开发语言·重构·rust
Hello.Reader2 天前
Rocket 0.5 响应体系Responder、流式输出、WebSocket 与 uri! 类型安全 URI
websocket·网络协议·安全·rust·rocket
FreeBuf_2 天前
黑客利用React Native CLI漏洞(CVE-2025-11953)在公开披露前部署Rust恶意软件
react native·react.js·rust
鸿乃江边鸟2 天前
Spark Datafusion Comet 向量化Rust Native--Native算子(CometNativeExec)怎么串联执行
大数据·rust·spark·native
mit6.8242 天前
[]try catch no | result yes
rust
Ivanqhz2 天前
向量化计算
开发语言·c++·后端·算法·支持向量机·rust