You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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是否指向同一个底层对象,应该使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 00:09:52