当前位置: 首页 > 知识库问答 >
问题:

如何在 不复制矢量的情况下将 Vec 转换为 Vec?

胡璞瑜
2023-03-14

我想转换一个Vec

我尝试了这样的方法

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>);
}

我不想复制向量,我想将u32字段包装在newtypeFoo

这给出了错误:

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)

共有2个答案

解柏
2023-03-14

根据< code > STD::mem::transduce()的文档,从Rust 1.38开始,使用< code>Vec::from_raw_parts结合< code>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())
};

这样做的先决条件是 TU 具有相同的大小、相同的最小对齐方式,并且对 T 有效的所有位模式也对 U 有效。如果您在问题中定义 TU,则无法保证这一点。

struct U(T)定义了一个元组结构,这种结构的内存布局是完全未定义的。但是,通过使用透明表示,可以强制内存表示相同:

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

Nightly Rust拥有<code>Vec::into_raw_parts</code>,这减少了代码量和出错的地方:

#![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) }
}
钮兴安
2023-03-14
匿名用户

不能在“安全保护”中就地更改值的类型。无法保证这两种类型具有相同的大小、对齐方式或语义。

这适用于单个值(< code>T)

最直接的解决方案:

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

在编译器可以判断您实际上没有更改重要内容的情况下,这实际上变成了无操作:

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

通过优化x86_64 Rust 1.54 的程序集输出:

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

不幸的是,这种轻量级转换目前不是一种保证属性,只是一个实现细节。

您可以调整< code > do _ something _ using _ Foo 来接受一个泛型类型,并让< code>Foo和< code>u32实现一个特征:

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);
}

这在技术上是可能的 - 你可以随心所欲地向自己开枪。

如果您知道自己在做什么,可以使用std::mem::transmute。

但是,使用<code>transmute</code>和<code>Vec</code>是未定义的行为,因为未定义<code>Vec</code>的表示。相反,请参见Sven Marnach的答案。

另请参阅:

    < li >使用矢量地图 < li >转换Vec

 类似资料:
  • 我需要这样的东西: 当然,我认为只有内置数字类型的向量才能传递给函数。 编译器告诉我,我需要为Vec提供FromIterator的trait trait

  • 我在用org。乔达。时间LocalDate和LocalDateTime。我从外部源获得一个Unix时间戳,并希望从中生成一个LocalDate(时间)。关键是,在该外部系统的界面中定义,所有日期/时间都在UTC时区内。因此,我希望避免从该时间戳到本地系统的任何默认时区的任何隐式转换,这可能与UTC不同。有一个LocalDateTime的构造器用于这些事情,所以我尝试(作为一个例子): 结果让我有点

  • 我有一个相机旋转矩阵3x3,然后我用罗德里格斯函数从旋转矩阵中得到旋转矢量,但是它给出的结果像,但是OpenGL函数需要度数。我认为是归一化的,我应该把每个元素乘以360来得到度数。但是OpenCV和OpenGL之间也有坐标系的差异。我如何在OpenGL坐标系中获得度数的旋转矢量?

  • 问题内容: 我需要一个解决方案将String转换为字节数组,而无需像这样更改: 输入: 输出: 当我使用 那么回复是 但我希望回复是 问题答案: 您应始终确保序列化和反序列化使用相同的字符集,这会将字符映射到字节序列,反之亦然。默认情况下,String.getBytes()和新的String(bytes)使用默认字符集,该字符集可能是特定于语言环境的。 使用getBytes(Charset)重载

  • 问题内容: 我有个问题。我正在尝试将一些字符串转换为日期,但我不知道日期到达的格式。 这或许让他们或等。 如何将这些字符串转换为Date?我尝试了这个: 但是,当我打印出someDate时,它的打印方式是这样的:2019-08-05 12:42:48.638 CEST这意味着,但是当我运行以上代码时,日期对象现在变成了,至少可以这样说。 有什么想法可以正确格式化日期格式吗? 问题答案: 你不能!

  • 我有代码,工作完全按要求。但是,我们的企业构建服务器拒绝任何带有编译器警告的签入。 由于我没有使用await语句,因此(如预期的那样)对于带有操作to Func转换的操作构造函数显示了以下警告。 更新-建议答案#1 _operation=()=>new Task(operation.invoke); _rollback=()=>new Task(rollback.invoke); 更新-接受的答案