Rust 如何比较“弧”中的特征对象?

Rust 如何比较“弧”中的特征对象?,rust,comparison,Rust,Comparison,让我们假设我有以下的背景: let a : Arc<dyn SomeTrait> = getA(); let b : Arc<dyn SomeTrait> = getB(); 检查特质对象相等性的推荐方法是什么 检查特质对象相等性的推荐方法是什么 除了“不要那样做”之外,我不确定有没有。可能的替代方案: 将操作添加到trait,作为内在操作或通过向trait公开的实例(例如UUID)添加唯一标识符 使用夜间和,并检查数据成员是否相同 我认为有必要解释为什么比较可能是

让我们假设我有以下的背景:

let a : Arc<dyn SomeTrait> = getA();
let b : Arc<dyn SomeTrait> = getB();
检查特质对象相等性的推荐方法是什么

检查特质对象相等性的推荐方法是什么

除了“不要那样做”之外,我不确定有没有。可能的替代方案:

  • 将操作添加到trait,作为内在操作或通过向trait公开的实例(例如UUID)添加唯一标识符
  • 使用夜间和,并检查
    数据
    成员是否相同

我认为有必要解释为什么比较可能是不明智的,因为根据您的用例,您可能不关心这些问题

像(fat)指针比较这样简单的东西被认为是反模式的主要原因是这样的比较可能会产生令人惊讶的结果;对于布尔测试,只有两种不直观的情况:

  • 误报(两个预期不同的事物仍然比较相等)

  • 错误否定(两个预期相等的事物最终不相等)

显然,这里的一切都与期望有关。执行的测试是指针相等:

  • 大多数人会认为,如果两个指针指向相同的数据,那么它们应该进行相等的比较…这在谈论fat指针时不一定如此。因此,这肯定会导致假阴性

  • 一些人,特别是那些不习惯于零大小类型的人,可能还认为两个不同的实例必须存在于不相交的内存中,“因此”具有不同的地址。但是(零大小类型的实例)不可能重叠,即使它们位于同一地址,因为这种重叠将是零大小的!这意味着您可以在同一地址拥有“不同的此类实例”(顺便说一句,这也是许多语言不支持零大小类型的原因:失去唯一地址的这个属性有其自身的警告)。因此,不知道这种情况的人可能会观察到误报

例子 两个fat指针可以有相等的数据指针,但比较时却不相等
  • 有一个非常基本的例子。考虑:

    让arr=[1,2,3];
    让all=&arr[…];//len=3,data_ptr=arr.as_ptr()
    让第一个=&arr[…1];//len=1,data_ptr=arr.as_ptr()
    断言!(::core::ptr::eq(全部,第一));//失败!
    
    这是一个基本示例,我们可以看到绑定在fat指针内的额外元数据(因此它们被称为“fat”)可能与数据指针“独立”地变化,从而导致这些fat指针随后不相等

现在,该语言中fat指针的唯一其他实例是(指向)
dyn Trait
s/Trait对象的指针)。这些fat指针携带的元数据是对一个结构的引用,该结构主要包含trait的特定方法(如
fn
指针),该trait对应于现在已擦除的原始数据类型:虚拟方法表,也称为vtable

每次将指向具体类型的指针(因此很小)强制为胖指针时,编译器都会自动生成这样的引用:

&42_i32//(细长)指向整数42的指针
as&dyn Display//编译器通过嵌入
//对i32的'impl Display'vtable的引用`
事实证明,当编译器,或者更准确地说,当前编译单元执行此操作时,它会创建自己的vtable

这意味着,如果不同的编译单元执行这种强制,可能会涉及多个vtable,因此对它们的引用可能并不都相等

我确实能够在下面(ab)使用事实
src/{lib,main}的游乐场中重现这一点。rs
是单独编译的

在我撰写本文时,该游乐场出现以下故障:

thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `0x5567e54f4047`,
 right: `0x5567e54f4047`', src/main.rs:14:9
如您所见,数据指针是相同的,
assert\u eq错误消息仅显示这些(fat指针的
调试
impl不显示元数据)


两个不同的对象可能位于同一地址 非常简单的展示:

让box1=Box::new(());//零规模“分配”
让box2=Box::new(());//同上
让vec=vec![(), ()];
让我们在第1栏:*常数()=&*第1栏;
让我们在第2栏:*常数()=&*第2栏;
let at_vec0:*const()=&vec[0];
let at_vec1:*const()=&vec[1];
断言!(at_vec0,at_vec1);//放心。
断言!(在第1箱,第2箱);//很可能。
断言!(在第0栏,在第1栏);//很可能。

结论? 既然您知道了胖指针比较的注意事项,那么您仍然可以选择执行比较(有意地使clippy lint静音),例如,如果您的所有
Arc
实例都在代码中的一个地方“胖”(强制为
dyn
)(这避免了来自不同vtables的错误否定),如果没有涉及零大小的实例(这可以避免误报)

例如:

mod lib{
用法::std::rc::rc;
酒吧
性状MyTrait{/*…*/}
i32{/*…*/}的impl MyTrait
字符串{/*…*/}的impl MyTrait
#[衍生(克隆)]
酒吧
结构MyType{
private:Rc(实例:T)
->选择权
{
if::core::mem::size_of::()==0{
没有一个
}否则{
一些(Self{private:Rc::new(instance)/*作为Rc*/})
}
}
pub fn new_i32(i:i32)->选项{Self::new(i)}
pub fn new_string(s:string)->选项{Self::new(s)}
酒吧
fn ptr_eq(自身和我的类型,其他和我的类型)
->布尔
{
//可能还可以;vtables都是在同一个mod中创建的
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `0x5567e54f4047`,
 right: `0x5567e54f4047`', src/main.rs:14:9