如何使用(不安全)别名?

时间:2015-05-22 22:37:47

标签: rust strict-aliasing borrow-checker

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

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

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的安全子集中​​,我必须有两个近乎副本&amp;函数fn(&mut)fn(&, &mut)的粘贴版本。

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

4 个答案:

答案 0 :(得分:5)

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

  

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

你称之为优化,我称之为悲观。

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

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

然而,在安全的Rust中,此信息可用。它会强制您编写两个例程(一个用于单个输入,一个用于两个输入),但两者都可以相应地进行优化。

答案 1 :(得分:4)

Rust不允许你参数化mutabilty,no。

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

&mut表示指针没有别名,优化器会将其视为等效。使用一个原始指针和一个&mut指针仍然可能导致问题。

答案 2 :(得分: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]

答案 3 :(得分:2)

您的主要功能必须使用不安全的代码才能使用原始指针。原始指针允许您绕过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()slices上的方法。