Reputation: 511
Consider the following example (playground link):
struct Wrapper(String);
async fn foo(my_slice: &[Wrapper]) {
bar(my_slice).await; // Error!
}
async fn bar(string_slice: &[String]) { ... }
I'm having difficulty writing foo
such that I can call bar(my_slice)
without cloning my_slice
, as String
doesn't implement Copy
.
I'm aware of the unsafe method to do this: Put a #[repr(transparent)]
on Wrapper
and mem::transmute
it, but I'm looking for ways to do this in entirely safe Rust.
I've attempted to implement From
, but slices since are always considered a foreign type and thus I've ran into orphan rules (foreign trait implementation on a foreign type) that prohibit me from doing so.
Unfortunately, I only have the ability to modify foo
or Wrapper
as bar
is auto-generated.
Is there any way to safely cast &[Wrapper]
to &[String]
without a clone?
Upvotes: 6
Views: 1803
Reputation: 71005
There is nothing in std (looking forward to safe transmute) but you can use the bytemuck
crate to do that safely:
#[derive(bytemuck::TransparentWrapper)]
#[repr(transparent)]
struct Wrapper(String);
async fn foo(my_slice: &[Wrapper]) {
bar(bytemuck::TransparentWrapper::peel_slice(my_slice)).await;
}
Upvotes: 1
Reputation: 27925
No. The type system has no predicate to represent the idea of "can be safely transmuted", so if your type is not one that can be coerced by the compiler itself, you must use unsafe
to do so.
However, you shouldn't use transmute
for what is effectively a pointer cast. Instead, decompose the slice into a pointer and a length and make a new slice with the target type.
#[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;
}
This is slightly more verbose than using transmute
, but it is also more limited in what it can do. transmute
is a general purpose tool that requires even more than the usual amount of care; save it for the cases where simple casts don't work.
I'm looking for ways to do this in entirely safe Rust.
Sticking to safe Rust is often a good idea; however, never using unsafe
means giving up some degree of performance and flexibility in exchange for not having to think too hard in situations like this. This looks like a perfectly fine use of unsafe
to me: it can be encapsulated in a tiny, safe function and it is easy to prove correct. But if you are determined to avoid unsafe
then there is no way around clone
ing the items.
Upvotes: 7