如何在不复制向量的情况下将Vec <T>转换为Vec <U>?

tve*_*eeg 6 rust

我想将a转换Vec<T>Vec<U>where T某种原语,并且U是一种新类型T:struct U(T).

我试过这样的事情:

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>);
}
Run Code Online (Sandbox Code Playgroud)

我不想复制矢量,我想u32在newtype中包装字段Foo.

这给出了错误:

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)
Run Code Online (Sandbox Code Playgroud)

She*_*ter 12

无法在安全Rust中更改值的类型.无法保证这两种类型具有相同的大小或相同的语义.

这适用于单个值(T- > U)以及聚合值(Vec<T>- > Vec<U>,HashMap<K1, V1>- > HashMap<K2, V2>).请注意,聚合值实际上只是"单个"值的特例.


最好的办法是创建一个新的向量:

let buffer2 = buffer.into_iter().map(Foo).collect();
Run Code Online (Sandbox Code Playgroud)

您还可以调整do_something_using_foo以采用兼具的通用泛型类型Foou32实现:

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);
}
Run Code Online (Sandbox Code Playgroud)

在不安全的Rust中,技术上是可行的 - 你可以根据自己的喜好自己射击脚.

你可以使用类似的东西,std::mem::transmute如果你知道你在做什么.

对于a transmute,我首先查看已弃用和已删除的函数Vec.请注意,它是一个170行的功能,充满了不安全的代码和图表,争论为什么它实际上是安全的.它已从标准库中删除,因为没有人想要维护它.

也可以看看:


Sve*_*ach 7

根据的文档std::mem::transmute(),从Rust 1.38开始,Vec::from_raw_parts结合使用ManuallyDrop是最好的选择:

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())
};
Run Code Online (Sandbox Code Playgroud)

这样做的先决条件是,T并且U具有相同的大小,相同的最小对齐方式,并且所有有效的位模式T也对有效U。如果您定义TU为你的问题,你没有这个保证。

struct U(T)定义一个元组结构,并且这种结构的内存布局是完全不确定的。但是,可以通过使用以下transparent表示来强制使内存表示相同:

#[repr(transparent)]
struct U(T);
Run Code Online (Sandbox Code Playgroud)

未来的可能性

Nightly Rust具有Vec::into_raw_parts,这减少了代码量和出错地方:

#![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) }
}
Run Code Online (Sandbox Code Playgroud)

还有一个开放的RFC 集合转换#2756,建议添加一种Vec::transmute方法。