5

Rust 有严格的别名规则。但是,如果“我知道自己在做什么”,我可以绕过它们吗?

我正在尝试将一个 C 函数转换为 Rust,该函数通过从输入缓冲区读取并写入目标缓冲区来执行复杂的操作,但它有一个巧妙的优化,允许输入和输出缓冲区相同:

foo(src, dst); // result is written to dst
foo(buf, buf); // legal in C, does the operation in-place

为了这个问题,让我们说它是这样的:

void inplace(char *src, char *dst, int len) {
   for(int i=0; i < len-1; i++) {
      dst[i] = src[i+1] * 2; // algorithm works even if src == dst
   }
}

在 Rust 的安全子集中​​,我必须有两个几乎复制和粘贴的函数版本fn(&mut)fn(&, &mut).

有没有办法欺骗 Rust 以获得对同一缓冲区的可变和不可变引用?

4

4 回答 4

6

不,你不能在安全的 Rust 中这样做。如果您愿意,可以使用不安全的代码来解决别名限制,但是......

但它有一个巧妙的优化,允许输入和输出缓冲区相同

你所说的优化,我称之为悲观。

当保证两个缓冲区不一样时,优化器可以向量化你的代码。这意味着循环的比较减少了 4 倍或 8 倍,大大加快了更大输入的执行速度。

然而,在没有混叠信息的情况下,它必须悲观地假设输入可能存在混叠,因此无法进行此类优化。更糟糕的是,不知道它们是如何别名的,它甚至不知道是&dst[i] == &src[i-1]or &dst[i] == &src[i]or &dst[i] == &src[i+1]; 这意味着预取已结束等...


然而,在安全的 Rust 中,这些信息是可用的。它确实迫使您编写两个例程(一个用于单个输入,一个用于两个输入),但都可以相应地进行优化。

于 2015-05-23T13:43:17.400 回答
5

Rust 不允许你对可变性进行参数化,不。

理论上,您可以编写一些不安全的代码给指针起别名,但您必须直接使用原始指针。

&mut意味着指针没有别名,优化器会这样对待它。使用一个原始指针和一个&mut指针仍然会导致问题。

于 2015-05-22T23:07:05.690 回答
4

您可以使用宏在安全代码中实现此目的。它适用于所有具有len函数并支持索引的参数。这基本上是鸭子打字。

macro_rules! inplace(
    ($a:ident, $b:ident) => (for i in 0..($a.len()-1) {
        $a[i] = $b[i + 1] * 2;
    })
);

fn main() {
    let mut arr = [1, 2, 3, 4, 5];
    inplace!(arr, arr);
    println!("{:?}", arr);
}

输出

[4、6、8、10、5]

于 2015-05-23T09:51:25.610 回答
2

您的 main 函数必须使用不安全的代码来实现才能使用原始指针。原始指针允许你绕过 Rust 的别名规则。然后,您可以使用两个函数作为此不安全实现的安全外观。

unsafe fn foo(src: *const u8, dst: *mut u8, len: usize) {
    for i in 0..len - 1 {
        *dst.offset(i as isize) = *src.offset(i as isize + 1) * 2;
    }
}

fn foo_inplace(buf: &mut [u8]) {
    unsafe { foo(buf.as_ptr(), buf.as_mut_ptr(), buf.len()) }
}

fn foo_separate(src: &[u8], dst: &mut [u8]) {
    assert!(src.len() == dst.len());
    unsafe { foo(src.as_ptr(), dst.as_mut_ptr(), src.len()) }
}

fn main() {
    let src = &[0, 1, 2, 3, 4, 5];
    let dst = &mut [0, 0, 0, 0, 0, 0];

    let buf = &mut [11, 22, 33, 44, 55, 66];

    foo_separate(src, dst);
    foo_inplace(buf);

    println!("src: {:?}", src);
    println!("dst: {:?}", dst);
    println!("buf: {:?}", buf);
}

as_ptr(),as_mut_ptr()len()切片上的方法。

于 2015-05-22T23:17:12.753 回答