22

I want to convert a Vec<T> to a Vec<U> where T is a primitive of some sort and U is a newtype of T: struct U(T).

I tried something like this:

struct Foo(u32);

fn do_something_using_foo(buffer: &mut Vec<Foo>) {}

fn main() {
    let buffer: Vec<u32> = vec![0; 100];

    do_something_using_foo(&mut buffer as Vec<Foo>);
}

I don't want to make a copy of the vector, I want to wrap the u32 fields in the newtype Foo.

This gives the error:

error[E0308]: mismatched types
 --> main.rs:8:28
  |
8 |     do_something_using_foo(&mut buffer as Vec<Foo>);
  |                            ^^^^^^^^^^^^^^^^^^^^^^^ expected mutable reference, found struct `std::vec::Vec`
  |
  = note: expected type `&mut std::vec::Vec<Foo>`
         found type `std::vec::Vec<Foo>`
  = help: try with `&mut &mut buffer as Vec<Foo>`

error: non-scalar cast: `&mut std::vec::Vec<u32>` as `std::vec::Vec<Foo>`
 --> main.rs:8:28
  |
8 |     do_something_using_foo(&mut buffer as Vec<Foo>);
  |                            ^^^^^^^^^^^^^^^^^^^^^^^

error: aborting due to previous error(s)
tversteeg
  • 4,717
  • 10
  • 42
  • 77
  • 4
    Just to be clear: you need to access the `Vec` as a `&[U]`, `&mut [U]`, or a `&mut Vec`? (Hint: The slice alternatives are vastly simpler). – bluss Jan 17 '18 at 20:01
  • @bluss for my situation `&mut [U]` would be acceptable although `&mut Vec` is preferable. – tversteeg Jan 17 '18 at 20:07
  • 1
    If `&mut [U]` is acceptable, then [it's preferred over `&mut Vec`](https://stackoverflow.com/q/40006219/155423). – Shepmaster Jan 17 '18 at 20:10
  • 2
    There was a time long long ago where `Vec` had a `fn map_in_place(self, f: F) -> Vec` method, that would work when `T` and `U` were the same size. It was removed in Rust 1.4, but there seem to be a crate that does that. Probably overkill though in that case. – mcarton Jan 17 '18 at 22:09

2 Answers2

22

You cannot change the type of a value in place in safe Rust. There is no guarantee that the two types will have the same size, alignment, or semantics.

This applies to a single value (T -> U) as well as aggregate values (Vec<T> -> Vec<U>, HashMap<K1, V1> -> HashMap<K2, V2>). Note that aggregate values are really just a special case of "single" values.

Create a new vector

The most straight-forward solution:

let buffer2 = buffer.into_iter().map(Foo).collect();

In cases where the compiler can tell that you aren't actually changing important things, this becomes effectively a no-op:

fn convert(b: Vec<u32>) -> Vec<Foo> {
    b.into_iter().map(Foo).collect()
}

x86_64 assembly output for Rust 1.54 with optimizations:

playground::convert:
    movq    %rdi, %rax
    movq    (%rsi), %rcx
    movups  8(%rsi), %xmm0
    movq    %rcx, (%rdi)
    movups  %xmm0, 8(%rdi)
    retq

Unfortunately, this lightweight transformation isn't currently a guaranteed property, only an implementation detail.

Use generics

You could adjust do_something_using_foo to take in a generic type and have both Foo and u32 implement a trait:

use std::borrow::{Borrow, BorrowMut};

#[derive(Debug, Clone)]
struct Foo(u32);

impl Borrow<u32> for Foo {
    fn borrow(&self) -> &u32 {
        &self.0
    }
}

impl BorrowMut<u32> for Foo {
    fn borrow_mut(&mut self) -> &mut u32 {
        &mut self.0
    }
}

fn do_something_using_foo<T>(buffer: &mut [T])
where
    T: BorrowMut<u32>,
{
}

fn main() {
    let mut buffer_u32 = vec![0u32; 100];
    let mut buffer_foo = vec![Foo(0); 100];

    do_something_using_foo(&mut buffer_u32);
    do_something_using_foo(&mut buffer_foo);
}

Unsafe Rust

It's technically possible — you can shoot yourself in the foot as much as you'd like.

You can use something like std::mem::transmute if you know what you are doing.

However, it's undefined behavior to use transmute with Vec as the representation of Vec is not defined. Instead, see Sven Marnach's answer.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • 1
    *There is no guarantee that the two types will have the same size*. At least, this can be verified: `std::mem::size_of`. – Boiethios Jan 18 '18 at 08:56
  • For a no-op in-place conversion, you don't need anything nearly as complex as the old `map_in_place()`. The reason for its complexity was mainly that it had to deal with the intermediate states when parts of the vector already have type `U` and the remainder still is type `T`. Getting this right even in the event of panics is quite involved. If the source and destination types have the same memory layout though, and you only want to _reinterpret_ the memory as a different type, then this can be done relatively easily. – Sven Marnach Mar 09 '19 at 21:13
  • The docs say that `transmute` is "equivalent to C’s memcpy under the hood", so I don't think this satisfies the criteria of "without copying the vector". I mention this because it's a high ranking Google result for "rust transmute without copying". – detly May 10 '21 at 00:22
  • 1
    @detly yes, the **24 bytes** (on 64-bit machine) of the `Vec` are copied. The data is not copied. Otherwise you'd need something like converting the 8 bytes of `&Vec`, which would still involve copying those 8 bytes. – Shepmaster May 10 '21 at 13:50
  • I misread the docs then; I thought it would copy the entire underlying slice of `T` as well. Does this mean that transmuting a slice only copies the data that makes up the fat pointer, and not the contiguous array of data it points to? – detly May 10 '21 at 14:36
15

According to the documentation of std::mem::transmute(), using Vec::from_raw_parts combined with ManuallyDrop is the best option, as of Rust 1.38:

let v_from_raw = unsafe {
    // Ensure the original vector is not dropped.
    let mut v_clone = std::mem::ManuallyDrop::new(v_orig);
    Vec::from_raw_parts(v_clone.as_mut_ptr() as *mut U,
                        v_clone.len(),
                        v_clone.capacity())
};

The prerequisite for this is that T and U have the same size, the same minimum alignment and that all bit patterns that are valid for T are also valid for U. If you define T and U as in your question, you don't have a guarantee for this.

struct U(T) defines a tuple struct, and the memory layout of such a struct is completely undefined. However, it is possible to force the memory representations to be identical by using the transparent representation:

#[repr(transparent)]
struct U(T);

Future options

Nightly Rust has Vec::into_raw_parts, which reduces the amount of code and places to go wrong:

#![feature(vec_into_raw_parts)]

fn convert_using_into_raw_parts(v: Vec<T>) -> Vec<U> {
    let (ptr, len, cap) = v.into_raw_parts();
    unsafe { Vec::from_raw_parts(ptr as *mut U, len, cap) }
}
Sven Marnach
  • 574,206
  • 118
  • 941
  • 841