在编写必须返回值的函数时,有两种类似的方法:
#1(从rustbyexample中提取)
// An integer division that doesn't `panic!`
fn checked_division(dividend: i32, divisor: i32) -> Option<i32> {
if divisor == 0 {
// Failure is represented as the `None` variant
None
} else {
// Result is wrapped in a `Some` variant
Some(dividend / divisor)
}
}
Run Code Online (Sandbox Code Playgroud)
#2(上述变体)
// An integer division that doesn't `panic!`
fn checked_division(dividend: i32, divisor: i32) -> Option<i32> {
if divisor == 0 {
// Failure is represented as the `None` variant
return None
}
// Result is wrapped in a `Some` variant
Some(dividend / divisor)
}
Run Code Online (Sandbox Code Playgroud)
我曾经写过像第二个片段,但我已经看到在Rust编程语言的每个例子中或者Rust By Example他们使用第一个案例.考虑到如上所述的匹配所有可能性代码,它只是造型还是存在性能差异?第一个是好的做法还是完全取决于我?
在Rust操场上,您可以使用ASM和LLVM IR按钮查看某些代码如何编译为汇编程序(机器代码)或LLVM的中间表示.LLVM IR通常更容易阅读,因为它比汇编程序更高级.
use std::io::BufRead;
// An integer division that doesn't `panic!`
#[inline(never)]
fn checked_division(dividend: i32, divisor: i32) -> Option<i32> {
if divisor == 0 {
// Failure is represented as the `None` variant
None
} else {
// Result is wrapped in a `Some` variant
Some(dividend / divisor)
}
}
// An integer division that doesn't `panic!`
#[inline(never)]
fn checked_division2(dividend: i32, divisor: i32) -> Option<i32> {
if divisor == 0 {
// Failure is represented as the `None` variant
return None
}
// Result is wrapped in a `Some` variant
Some(dividend / divisor)
}
fn main() {
let stdin = std::io::stdin();
let i: i32 = stdin.lock().lines().next().unwrap().unwrap().parse().unwrap();
let j: i32 = stdin.lock().lines().next().unwrap().unwrap().parse().unwrap();
println!("{:?}", checked_division(i, j));
println!("{:?}", checked_division2(i, j));
}
Run Code Online (Sandbox Code Playgroud)
(注意:我正在执行I/O以获取编译器无法优化的值;常量过于激进而且checked_division函数完全消失,即使使用#[inline(never)].)
首先,让我们在发布模式下编译此代码.LLVM IR是什么样的?这是checked_division:
; Function Attrs: noinline uwtable
define internal fastcc i64 @_ZN16checked_division20h2cc10ba72e80f410faaE(i32, i32) unnamed_addr #0 {
entry-block:
switch i32 %1, label %next1 [
i32 0, label %join
i32 -1, label %cond2
]
next1: ; preds = %entry-block, %cond2
%2 = sdiv i32 %0, %1
%phitmp = zext i32 %2 to i64
%phitmp5 = shl nuw i64 %phitmp, 32
br label %join
cond2: ; preds = %entry-block
%3 = icmp eq i32 %0, -2147483648
br i1 %3, label %cond4, label %next1
cond4: ; preds = %cond2
tail call void @_ZN9panicking5panic20h77d028a733b1a80eiEKE({ %str_slice, %str_slice, i32 }* noalias nonnull readonly dereferenceable(40) @panic_loc3962)
unreachable
join: ; preds = %entry-block, %next1
%sret_slot.sroa.0.0 = phi i64 [ 1, %next1 ], [ 0, %entry-block ]
%sret_slot.sroa.3.0 = phi i64 [ %phitmp5, %next1 ], [ 0, %entry-block ]
%4 = or i64 %sret_slot.sroa.3.0, %sret_slot.sroa.0.0
ret i64 %4
}
Run Code Online (Sandbox Code Playgroud)
这是checked_division2:
; Function Attrs: noinline uwtable
define internal fastcc i64 @_ZN17checked_division220h9ae6c6af45a9a593DaaE(i32, i32) unnamed_addr #0 {
entry-block:
switch i32 %1, label %next1 [
i32 0, label %return
i32 -1, label %cond2
]
next1: ; preds = %entry-block, %cond2
%2 = sdiv i32 %0, %1
%phitmp = zext i32 %2 to i64
%phitmp5 = shl nuw i64 %phitmp, 32
br label %return
return: ; preds = %entry-block, %next1
%sret_slot.sroa.0.0 = phi i64 [ 1, %next1 ], [ 0, %entry-block ]
%sret_slot.sroa.3.0 = phi i64 [ %phitmp5, %next1 ], [ 0, %entry-block ]
%3 = or i64 %sret_slot.sroa.3.0, %sret_slot.sroa.0.0
ret i64 %3
cond2: ; preds = %entry-block
%4 = icmp eq i32 %0, -2147483648
br i1 %4, label %cond4, label %next1
cond4: ; preds = %cond2
tail call void @_ZN9panicking5panic20h77d028a733b1a80eiEKE({ %str_slice, %str_slice, i32 }* noalias nonnull readonly dereferenceable(40) @panic_loc3964)
unreachable
}
Run Code Online (Sandbox Code Playgroud)
如果你比较你最喜欢的diff工具中的两个函数(并排diff工具在这里更好,因为那里有一点噪音),你会注意到唯一的主要区别是checked_division有一个块join在最后调用,而checked_division2有一个称为块return之间next1和cond2-但这些块的内容是相同的.换句话说,功能完全相同.
我们可以注意到的另一件事是功能还是恐慌,如果你尝试执行-2147483648/-1(-1测试的一部分switch,在开始时,-2147483648测试是对下cond2:). That's because this particular division overflows, and [LLVM'ssdiv`指令] 3个文档这种情况下,导致未定义的行为,因此Rust编译器通过恐慌来为您的函数提供明确定义的行为.
| 归档时间: |
|
| 查看次数: |
83 次 |
| 最近记录: |