无需克隆即可安全地将新型切片转换为内部变体

Edw*_*hen 4 rust

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

struct Wrapper(String);

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

async fn bar(string_slice: &[String]) { ... }
Run Code Online (Sandbox Code Playgroud)

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

我知道不安全的方法来做到这一点的:将一个#[repr(transparent)]Wrappermem::transmute它,但是我正在寻找方式完全安全的锈做到这一点。

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

不幸的是,我只需要修改的能力fooWrapperbar是自动生成的。

有没有办法&[Wrapper]&[String]没有克隆的情况下安全地投射到?

tre*_*tcl 5

不。类型系统没有谓词来表示“可以安全地转换”的想法,因此如果您的类型不是编译器本身可以强制转换的类型,则必须使用它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;
}
Run Code Online (Sandbox Code Playgroud)

这比 using 稍微冗长一些transmute,但它可以做的事情也更有限。transmute是一种通用工具,需要比平时更多的照顾;将其保存在简单强制转换不起作用的情况下。

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

坚持使用安全的 Rust 通常是个好主意;然而,从不使用unsafe意味着放弃一定程度的性能和灵活性,以换取在这种情况下不必考虑太多。unsafe对我来说,这看起来是一个完美的用法:它可以封装在一个微小的、安全的函数中,并且很容易证明是正确的。但是,如果您决心避免unsafe,则无法绕过clone这些项目。

  • @xes_p 我不知道有什么特别活跃的事情,但已经讨论过了;[这里](https://internals.rust-lang.org/t/pre-rfc-v2-safe-transmute/11431) 是一个半新的线程,您可以从中开始。尽管验证像您这样的小案例很简单,但当您尝试思考如何更普遍地确保“transmute”安全时,就会开始出现“很多”陷阱。“repr(transparent)”虽然是必要的,但只是难题的一小部分。 (2认同)