Rust中内存地址一致但Arc指针比较失败问题求助
Rust中Arc胖指针地址显示相同但assert_eq!失败的原因及解决方法
在构建依赖链时,使用Weak指针指向链中的下一个Target对象,升级Weak为Arc后,发现即使{:p}输出的内存地址相同,assert_eq!比较仍失败。复现代码及错误信息如下:
use std::{ fmt::Debug, sync::{ Arc, Mutex, Weak, }, }; pub trait Target: Debug + Send + Sync { fn get_target(&self) -> Result<Option<Arc<Mutex<dyn Target>>>, String>; } #[derive(Debug)] pub struct Parent { pub id: u32, pub target: Option<Weak<Mutex<dyn Target>>> } impl Parent { pub fn assign_target<T: Target + Debug + Send + Sync + 'static>(&mut self, target: &Arc<Mutex<T>>) { self.target = Some(Arc::downgrade(&(target.clone() as Arc<Mutex<dyn Target>>))); } } impl Target for Parent { fn get_target(&self) -> Result<Option<Arc<Mutex<dyn Target>>>, String> { if let None = self.target { return Ok(None); } match self.target.as_ref().unwrap().upgrade() { Some(target_pointer) => Ok(Some(target_pointer)), None => Err("Target object was dropped".to_string()) } } } #[derive(Debug)] pub struct Child { pub id: u32 } impl Target for Child { fn get_target(&self) -> Result<Option<Arc<Mutex<dyn Target>>>, String> { Ok(None) } } fn main() { // Create object instances let child = Arc::new(Mutex::new(Child { id: 4 })); let parent = Arc::new(Mutex::new(Parent { id: 2, target: None })); // Assign targets parent.try_lock().unwrap().assign_target(&child); // Print pointer memory addresses println!("Original pointer: {:p}", Arc::as_ptr(&child)); println!("Upgraded pointer assigned to parent: {:p}", Arc::as_ptr(&parent.try_lock() .unwrap() .get_target() .unwrap() .unwrap())); println!("Cloned and casted pointer: {:p}", Arc::as_ptr(&(child.clone() as Arc<Mutex<dyn Target>>))); println!(""); // Compare the target assigned and a direct // casted clone of the original object. assert_eq!(Arc::as_ptr(&parent.try_lock().unwrap().get_target().unwrap().unwrap()), Arc::as_ptr(&(child.clone() as Arc<Mutex<dyn Target>>))); }
运行后错误信息:
Original pointer: 0x17308dcab70 Upgraded pointer assigned to parent: 0x17308dcab70 Cloned and casted pointer: 0x17308dcab70 thread 'main' panicked at src\main.rs:68:5: assertion `left == right` failed left: 0x17308dcab70 right: 0x17308dcab70 note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
问题原因
你遇到的核心问题是胖指针(Wide Pointer)的比较逻辑与格式化输出的差异:
Arc<Mutex<dyn Target>>中的dyn Target是 trait 对象,对应的*const Mutex<dyn Target>是一个胖指针,包含两部分:指向实际数据的指针,以及指向 trait vtable 的指针。{:p}格式化输出时,只会打印胖指针中的数据指针部分,所以看起来地址完全相同。- 但
assert_eq!比较胖指针时,会同时校验数据指针和vtable指针是否都一致。你的代码中,左右两边的胖指针虽然数据指针相同,但vtable指针来源不同:- 左边的Arc是从Weak升级而来,Weak是由
Arc<Mutex<dyn Target>>直接降级得到的,其vtable指向Mutex<dyn Target>的 trait 表。 - 右边的Arc是通过
Arc<Mutex<Child>>强制转换为Arc<Mutex<dyn Target>>得到的,其vtable是从Mutex<Child>coerce 而来的,与左边的vtable指针并非同一地址。
- 左边的Arc是从Weak升级而来,Weak是由
解决方法
要验证两个Arc是否指向同一个底层对象,应该使用Arc提供的ptr_eq方法,它会直接比较Arc内部的引用计数指针(确保指向同一个堆上的ArcInner结构体),而不是比较数据指针:
修改断言代码如下:
fn main() { // ... 原有代码 ... let left_arc = parent.try_lock().unwrap().get_target().unwrap().unwrap(); let right_arc = child.clone() as Arc<Mutex<dyn Target>>; // 使用Arc::ptr_eq判断是否指向同一个对象 assert!(Arc::ptr_eq(&left_arc, &right_arc)); }
如果确实需要比较数据指针,可以手动提取胖指针中的数据部分(需使用unsafe操作):
use std::ptr; fn main() { // ... 原有代码 ... let left_arc = parent.try_lock().unwrap().get_target().unwrap().unwrap(); let right_arc = child.clone() as Arc<Mutex<dyn Target>>; let left_ptr = Arc::as_ptr(&left_arc); let right_ptr = Arc::as_ptr(&right_arc); // 提取胖指针中的数据部分 let left_data_ptr = unsafe { ptr::addr_of!((*left_ptr).data) }; let right_data_ptr = unsafe { ptr::addr_of!((*right_ptr).data) }; assert_eq!(left_data_ptr, right_data_ptr); }
这种方法需要unsafe操作,且不如Arc::ptr_eq直观,推荐优先使用前者。
内容的提问来源于stack exchange,提问作者RodoGM
相关产品推荐
相关产品推荐

