让我们从困难的事情开始吧.如果你想要一个简单的解决方案,跳到答案的末尾(最后一节).
因此:事实是,它确实可以奏效.为什么不是呢?好吧,显然有一些问题,但主要问题可能是没有人致力于这一点.There is a thread in internals.rust-lang.org discussing that.
首先,我们需要了解coercion和subtyping之间的区别.
Coercing类型T
到U
意味着当你有一个类型为T
的东西,并且你想把它转换成类型U
,编译器可以自动为你完成这项工作.您只需要声明您想要类型U
,编译器就会施展它的魔力,为您提供它.例如,对数组(&[T; N]
)的引用可以被强制为对片(&[T]
)的引用.当您拥有对数组的引用并将其传递给需要对片的引用的对象(例如函数)时,编译器会自动插入代码以将指向数组的指针(在汇编代码中引用数组)转换为指针+大小(在汇编代码中对片的引用).
一般来说,每一次转变都可能是一种胁迫.它只需要内置到语言中,编译器必须知道如何插入代码才能做到这一点.然而,Rust的原则是expensive things should be explicit,因此,胁迫(隐含的)需要便宜.
对我们来说,理解胁迫的重要一点是they're not transitive.也就是说,如果类型T
可以被强制为U
,这并不意味着G<T>
可以被强制为G<U>
.或者,简单地说,&[T; N]
可以被强迫到&[T]
的事实并不意味着我们也可以强迫Vec<&[T; N]>
到Vec<&[T]>
.原因很简单:因为强制需要实际插入代码来执行转换,所以我们不知道如何为包装器类型生成代码.我们知道将&[T; N]
转换为&[T]
(通过简单地附加大小)并不意味着我们知道将&[T; N]
s的矢量转换为&[T]
s的矢量,这需要分配和复制(因为&[T]
的大小是&[T; N]
的两倍).
subtyping relationship是完全不同的一件事.如果类型T
是U
的子类型,那就意味着any 100 is also 101.OOP语言中的基类就是一个典型的例子.Derived
的实例也是Base
的实例.这确实意味着,与强制一样,指向Derived
的指针可以转换为指向Base
的指针,但与强制不同的是,指向this doesn't require inserting any conversion code的指针可以转换为指向Base
的指针.
在 compose 本文时,Rust中唯一的子类型是终身关系.例如,'static
是任意生存期的子类型,这意味着&'static T
是任意&'a T
的子类型.这就是为什么你可以给&'static T
分,而你应该给&'a T
分.
因为不需要转换代码,所以子类型is是可传递的.您可以将Vec<&'static T>
转换为Vec<&'a T>
.但有一个例外,那就是差异,我现在就不谈了.
现在来看重要的一点:从dyn Trait + Send + Sync
到dyn Trait + Sync
的转换(换句话说,从dyn Trait
删除了额外的自动特征界限)是强制的,而不是子类型.这就是您无法将&Vec<Box<dyn Trait + Send + Sync>>
转换为&Vec<Box<dyn Trait + Sync>>
的原因--转换是不可传递的.
它没有理由不是子类型-转换不需要代码,除此之外,正如我已经说过的,没有人做过这方面的工作.
但我们还能这样做吗?
使用不安全的代码是的;但也不是.
句号,不允许从&Vec<Box<dyn Trait + Send + Sync>>
转换到&Vec<Box<dyn Trait + Sync>>
.即使假设Box<dyn Trait + Send + Sync>
和Box<dyn Trait + Sync>
具有相同的布局,也不能保证包含它们的两个Vec
也具有相同的布局.所以你所要求的是不可能实现的.
但是类似的东西怎么样:从Vec<Box<dyn Trait + Send + Sync>>
转换到Vec<Box<dyn Trait + Sync>>
(没有参考).或者类似地,&[Box<dyn Trait + Send + Sync>]
到&[Box<dyn Trait + Sync>]
.或者甚至是&Vec<Box<dyn Trait + Send + Sync>>
到&[&(dyn Trait + Send + Sync)]
?
我们可以使用Vec::from_raw_parts()
(或std::slice::from_raw_parts()
)来构建Vec
(或片),而不依赖于其布局细节.但我们仍然需要指向dyn Trait + AutoTrait
的指针与指向dyn Trait
的指针具有相同的布局.这能行得通吗?
嗯,目前是这样的,我相信将来也会这样.但任何地方都没有记录,所以我不会依赖于这一点.
然而,从你的 comments 中,看起来你想要的只是一片&[&(dyn SomeTrait + Sync)]
,而你甚至不在乎你是否分配.嗯,这可以很容易地做到:
let v2: Vec<&(dyn SomeTrait + Sync)> = v.iter().map(|item| &**item as _).collect();
let v2: &[&(dyn SomeTrait + Sync)] = v2.as_slice();