开启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"
相关推荐
勇敢牛牛_5 小时前
【Rust笔记】Rocket实现自定义的Responder
开发语言·笔记·rust
好看资源平台5 小时前
Cargo 的工作机制
开发语言·rust
陈序缘5 小时前
Rust实现Kafka - 前言
开发语言·分布式·后端·职场和发展·rust·kafka
喜欢打篮球的普通人5 小时前
2024 Rust现代实用教程:1.3获取rust的库国内源以及windows下的操作
开发语言·windows·rust
TYYJ-洪伟5 小时前
Rust 程序设计语言学习——高级特性
rust··指针·函数指针·闭包·不安全
陈序缘5 小时前
Rust 力扣 - 48. 旋转图像
开发语言·后端·算法·leetcode·职场和发展·rust
小宇学编程5 小时前
M1 Pro MacBook Pro 上的奇遇:Rust 构建失败,SIGKILL 惊魂记
后端·rust·编译问题·编译失败·cargo build
老猿讲编程21 小时前
Rust语言的优缺点以及学习建议
开发语言·学习·rust
勇敢牛牛_21 小时前
【Rust基础】创建第一个Rust项目
开发语言·rust
新知图书1 天前
Rust编程与项目实战-元组
开发语言·算法·rust