在不进行克隆的情况下,将一个新类型切片安全地强制转换为内部变体



考虑以下示例(游乐场链接(:

struct Wrapper(String);
async fn foo(my_slice: &[Wrapper]) {
bar(my_slice).await; // Error!
}
async fn bar(string_slice: &[String]) { ... }

我在编写foo时遇到了困难,这样我就可以在不克隆my_slice的情况下调用bar(my_slice),因为String没有实现Copy

我知道这样做的不安全方法:在Wrappermem::transmute上放一个#[repr(transparent)],但我正在寻找在完全安全的Rust中这样做的方法。

我曾尝试实现From,但切片一直被认为是外来类型,因此我遇到了禁止我这样做的孤立规则(外来类型上的外来特性实现(

不幸的是,我只能修改fooWrapper,因为bar是自动生成的。

有没有办法在没有克隆的情况下安全地将&[Wrapper]转换为&[String]

否。类型系统没有谓词来表示";可以安全地转化";,因此,如果您的类型不是编译器本身可以强制的类型,则必须使用unsafe来执行此操作

但是,对于实际上是指针强制转换的内容,不应该使用transmute。相反,将切片分解为一个指针和一个长度,并制作一个具有目标类型的新切片。

#[repr(transparent)]
struct Wrapper(String);
async fn foo(my_slice: &[Wrapper]) {
let my_slice =
unsafe { std::slice::from_raw_parts(my_slice.as_ptr() as *const String, my_slice.len()) };
bar(my_slice).await;
}

这比使用transmute稍微详细一些,但它的功能也更有限。transmute是一个通用工具,需要比通常更多的注意;将其保存到简单强制转换不起作用的情况。

我正在寻找在完全安全的Rust中做到这一点的方法。

坚持安全的Rust通常是个好主意;然而,never使用unsafe意味着放弃一定程度的性能和灵活性,以换取在这种情况下不必过于认真思考。对我来说,这看起来像是对unsafe的完美使用:它可以封装在一个微小的、安全的函数中,而且很容易证明它是正确的。但是,如果你决心避免unsafe,那么就没有办法绕过clone

std中没有任何内容(期待安全的转化(,但您可以使用bytemuck板条箱安全地做到这一点:

#[derive(bytemuck::TransparentWrapper)]
#[repr(transparent)]
struct Wrapper(String);
async fn foo(my_slice: &[Wrapper]) {
bar(bytemuck::TransparentWrapper::peel_slice(my_slice)).await;
}

最新更新