xes_p
xes_p

Reputation: 511

Safely cast a newtype slice into inner variant without cloning

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

Answers (2)

Chayim Friedman
Chayim Friedman

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

trent
trent

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 cloneing the items.

Upvotes: 7

Related Questions