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

Вектор может быть разыменован в срез одним из следующих способов:

  • let slice = &*my_vec;
  • let slice = &my_vec[..];

Я предпочитаю второе, хотя оно и более многословно, но нахожу его более ясным, особенно когда выражение смешано с плотно используемыми операторами и где разыменование имеет разные последствия в зависимости от Box/Vec/ указатель типов.

С другой стороны, он использует избыточный диапазон.

Я бы хотел игнорировать личные предпочтения в стиле кода и сосредоточиться на ощутимых различиях. Они когда-нибудь компилируются в другой код для сборок релиза?

2 ответа

Решение

Нет никакой разницы после оптимизации:

#[no_mangle]
extern {
    fn simple(ptr: *const u8, len: usize) -> usize;
}

fn take_slice(slice: &[u8]) {
    unsafe { simple(slice.as_ptr(), slice.len()); }
}

#[inline(never)]
fn take_vec_auto(v: &Vec<u8>) {
    take_slice(v);
}

#[inline(never)]
fn take_vec_deref(v: &Vec<u8>) {
    take_slice(&*v);
}

#[inline(never)]
fn take_vec_index(v: &Vec<u8>) {
    take_slice(&v[..]);
}

Приводит к следующему LLVM IR на детской площадке:

; Function Attrs: noinline nounwind uwtable
define internal fastcc void @_ZN8rust_out13take_vec_auto17h2827abd8ce79beacE(i8* %.0.0.0.0.0.val, i64 %.0.1.val) unnamed_addr #0 {
entry-block:
  %0 = tail call i64 @simple(i8* nonnull %.0.0.0.0.0.val, i64 %.0.1.val) #2
  ret void
}

; Function Attrs: noinline nounwind uwtable
define internal fastcc void @_ZN8rust_out14take_vec_deref17h66cf4ce954b36d1dE(i8* %.0.0.0.0.0.val, i64 %.0.1.val) unnamed_addr #0 {
entry-block:
  %0 = tail call i64 @simple(i8* nonnull %.0.0.0.0.0.val, i64 %.0.1.val) #2
  ret void
}

; Function Attrs: noinline nounwind uwtable
define internal fastcc void @_ZN8rust_out14take_vec_index17h77571b14bbdb120cE(i8* %.0.0.0.0.0.val, i64 %.0.1.val) unnamed_addr #0 {
entry-block:
  %0 = tail call i64 @simple(i8* nonnull %.0.0.0.0.0.val, i64 %.0.1.val) #2
  ret void
}

Так что это в основном вопрос стиля, а стиль субъективен.

Насколько я могу судить, основываясь на текущем режиме MIR ночного выпуска, первый вариант предпочтительнее, так как он на одно распределение меньше:

let mut _0: ();
scope 1 {
    let _1: std::vec::Vec<i32>;
    scope 2 {
        let _6: &[i32];
    }
}
let mut _2: ();
let mut _3: std::boxed::Box<[i32]>;
let mut _4: std::boxed::Box<[i32; 3]>;
let mut _5: std::boxed::Box<[i32; 3]>;
let mut _7: &[i32];
let mut _8: &std::vec::Vec<i32>;
let mut _9: std::ops::RangeFull; // not present in variant 1

Однако я не знаю, как это будет выглядеть после дальнейшей оптимизации - она ​​может отличаться в зависимости от целевого использования.

Другие вопросы по тегам