2015-05-22 2 views
5

Руста имеет строгие правила псевдонимов. Но могу ли я обойти их, если «я знаю, что я делаю»?Как использовать (небезопасное) псевдонимы?

Я пытаюсь преобразовать в Rust функцию C, которая выполняет сложную операцию, считывая из входного буфера и записывая в буфер назначения, но имеет умную оптимизацию, которая позволяет входному и выходному буферам быть одинаковыми:

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).

Есть ли способ обмануть ржавчину, чтобы получить как изменяемую, так и неизменную ссылку на тот же буфер?

+0

Разве у вас не будет двух разных функций, если у них есть другая семантика? –

+0

@RobertHarvey Semantic - то же самое: возьмите ввод от первого указателя и напишите вывод ко второму указателю. Есть только важная оптимизация, что буферы ввода и вывода могут быть одинаковыми. – Kornel

+1

Вы можете использовать '* const' и' * mut', но тогда вы потеряете все гарантии безопасности, относящиеся к этим частям памяти. Это не случайно, отсутствие изменчивого сглаживания является центральным для многих гарантий Руста. – delnan

ответ

2

Ваша основная функция должна быть реализована с использованием небезопасного кода, чтобы использовать необработанные указатели. Исходные указатели позволяют обходить правила псевдонимов Руста. Затем вы можете иметь две функции, которые действуют как безопасные фасады для этой небезопасной реализации.

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.

+1

Поскольку вы уже создаете фасад, вы можете использовать все, что захотите. Поэтому я рекомендую использовать [мое макро решение] (http://stackoverflow.com/a/30411267/1103681), чтобы сохранить это в безопасном коде. Вы никогда не должны использовать небезопасный код, если имеется безопасная версия кода. –

4

Ржавчина не позволяет параметризовать по мутабилити, нет.

В теории вы могли бы написать некоторые небезопасные коды, которые указатели на псевдонимы, но вам нужно будет напрямую использовать необработанные указатели.

&mut подразумевает, что указатель не является псевдонимом, и оптимизатор будет рассматривать его как таковой. Использование одного необработанного указателя и одного указателя &mut все равно может вызвать проблемы.

3

Вы можете использовать макрос для достижения этого в безопасном коде. Он будет работать для всех аргументов, которые имеют функцию 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]

4

Нет, вы не можете сделать это в безопасном Rust. Вы можете использовать небезопасный код, чтобы обойти ограничение наложения, если вы хотите, но ...

но имеют умную оптимизацию, которая позволяет входной и выходной буфер, чтобы быть такими же

что вы называете оптимизация, я называю пессимизацию.

Если два буфера гарантированно не совпадают, оптимизатор может векторизовать ваш код. Это означает, что сравнение в 4х или 8 раз меньше для цикла, что значительно ускоряет выполнение для больших входов.

В отсутствие информации о сглаживании, однако, пессимистично предположить, что входы могут быть сглажены и поэтому не могут выполнять такую ​​оптимизацию. Хуже, не зная , как они псевдонимы, он даже не знает, &dst[i] == &src[i-1] или &dst[i] == &src[i] или &dst[i] == &src[i+1]; это означает, что предварительная выборка отсутствует и т. д. ...


В надежной русте, однако, эта информация доступна. Это заставляет вас писать две подпрограммы (одна для одного входа, одна для двух входов), но обе они могут быть оптимизированы соответствующим образом.

Смежные вопросы