rust 安全地将newtype切片转换为内部变量而不进行克隆

vuktfyat  于 2023-05-18  发布在  其他
关注(0)|答案(2)|浏览(188)

考虑以下示例(playground link):

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
我知道这样做的不安全方法:将#[repr(transparent)]放在Wrappermem::transmute上,但我正在寻找在完全安全的Rust中做到这一点的方法。
我尝试过实现From,但是切片总是被认为是外来类型,因此我遇到了孤儿规则(在外来类型上实现外来trait),禁止我这样做。
不幸的是,我只能修改fooWrapper,因为bar是自动生成的。
有没有办法在不克隆的情况下安全地将&[Wrapper]转换为&[String]

tyky79it

tyky79it1#

类型系统没有 predicate 来表示“可以安全地转换”的思想,所以如果你的类型不能被编译器本身强制转换,你必须使用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

nkoocmlb

nkoocmlb2#

std中没有任何东西(期待safe transmute),但您可以使用bytemuck crate来安全地执行此操作:

#[derive(bytemuck::TransparentWrapper)]
#[repr(transparent)]
struct Wrapper(String);

async fn foo(my_slice: &[Wrapper]) {
    bar(bytemuck::TransparentWrapper::peel_slice(my_slice)).await;
}

相关问题