是否有任何实际考虑更喜欢将向量转换为切片的一种表示法?
Is there any practical considerations to prefer one notation for converting vectors into slices?
可以通过以下任一方式将向量取消引用到切片中:
let slice = &*my_vec;
let slice = &my_vec[..];
我更喜欢第二种,尽管它更冗长,但我发现它更清晰,尤其是当语句与频繁使用的运算符混合使用时,并且取消引用具有不同的含义,具体取决于 Box
/Vec
/指针类型。
另一方面,它使用了冗余范围。
我想忽略代码风格的个人偏好并关注有形差异。他们是否曾为发布版本编译成不同的代码?
据我所知,基于当前的每晚 release-mode 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
但是,我不知道进一步优化后会是什么样子 - 根据目标用途,这些可能会有所不同。
优化后严格没有区别:
#[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 on the playground:
; 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
}
所以这主要是风格问题,风格是主观的。
可以通过以下任一方式将向量取消引用到切片中:
let slice = &*my_vec;
let slice = &my_vec[..];
我更喜欢第二种,尽管它更冗长,但我发现它更清晰,尤其是当语句与频繁使用的运算符混合使用时,并且取消引用具有不同的含义,具体取决于 Box
/Vec
/指针类型。
另一方面,它使用了冗余范围。
我想忽略代码风格的个人偏好并关注有形差异。他们是否曾为发布版本编译成不同的代码?
据我所知,基于当前的每晚 release-mode 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
但是,我不知道进一步优化后会是什么样子 - 根据目标用途,这些可能会有所不同。
优化后严格没有区别:
#[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 on the playground:
; 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 }
所以这主要是风格问题,风格是主观的。