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。
我知道执行此操作的不安全方法:在Wrapper和mem::transmute上分别放置#[repr(transparent)],但是我正在寻找在完全安全的Rust中执行此操作的方法。
我曾尝试实现From,但由于切片始终被视为外来类型,因此我遇到了孤立规则(禁止在外来类型上实现外来特性)。<铅>
不幸的是,由于bar是自动生成的,所以我只能修改foo或Wrapper。
有没有办法在没有克隆的情况下安全地将&[Wrapper]强制转换为&[String]?
- 我会将函数的参数转换为迭代器,并使用map来获取内部字符串。尽管编译器是否将其优化为仅顺序读取切片还是个问题
-
&[T]的迭代器不会产生Iterator<Item = &T>吗?我认为,我仍然需要一个克隆来获得拥有的变体,或者提供一个拥有的变体(例如vec),这只会将克隆推送给调用者。
-
为什么您需要为自己的boo获取一个拥有的String?除非您确实需要使用字符串,否则通常的模式是使用&str
-
我同意,但是我正在使用生成的代码。更改该更改将在内部视为一个非常大的突破性更改,因此我无法更改此更改。我假设我们正在异步环境中使用String,因为&str将需要一个'static生存期。
不。类型系统没有谓词来表示"可以安全地转换"的想法,因此,如果您的类型不是可由编译器本身强制的类型,则必须使用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放置这些物品。
- 谢谢,我也看到了from_raw_parts,但是在有安全解决方案的情况下推迟了。您是否认为类似这样的东西可能是stdlib的一部分,例如通过RFC?
-
@xes_p我不知道有什么特别活跃的东西,但是已经讨论过了。这是您可以从中开始的一个半新线程。验证像您这样的琐碎案件很简单,当您尝试思考如何使transmute更安全时,就会出现很多陷阱。 repr(transparent),尽管有必要,但仅是难题的一小部分。
-
感谢您的链接,这绝对是我从未见过的有趣读物!