关于rust:无需克隆即可将新类型的切片安全地投射到内部变体中

Safely cast a newtype slice into inner variant without cloning

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

1
2
3
4
5
6
7
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,但由于切片始终被视为外来类型,因此我遇到了孤立规则(禁止在外来类型上实现外来特性)。<铅>

不幸的是,由于bar是自动生成的,所以我只能修改fooWrapper

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


不。类型系统没有谓词来表示"可以安全地转换"的想法,因此,如果您的类型不是可由编译器本身强制的类型,则必须使用unsafe来做到这一点。

但是,您不应将transmute用作有效的指针强制转换。而是将切片分解为指针和长度,然后使用目标类型创建一个新的切片。

1
2
3
4
5
6
7
8
#[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是一种通用工具,需要比平时更多的照护;将其保存以用于简单的强制转换不起作用的情况。

I'm looking for ways to do this in entirely safe Rust.

抵制生锈通常是个好主意;但是,从不使用unsafe意味着放弃某种程度的性能和灵活性,以换取在这种情况下不必费力思考。对我来说,这似乎很好地使用了unsafe:它可以封装在一个微小的安全函数中,并且很容易证明是正确的。但是,如果您决定避免使用unsafe,则无法绕过clone放置这些物品。