我发现我认为这是一种非常奇怪的行为。当变量在运行时溢出时,Rustc会发生混乱;这对我来说很有意义。但是,它只会在编译时分配溢出值时发出警告。那应该是编译时错误吗?否则,两种行为似乎不一致。
我期待编译时错误:
fn main() {
let b: i32 = 3_000_000_000;
println!("{}", b);
}
制作:
<anon>:2:18: 2:31 warning: literal out of range for i32, #[warn(overflowing_literals)] on by default
<anon>:2 let b: i32 = 3_000_000_000;
这对我有意义:
fn main() {
let b: i32 = 30_000;
let c: i32 = 100_000;
let d = b * c;
println!("{}", d);
}
产地:
thread '<main>' panicked at 'arithmetic operation overflowed', <anon>:4
playpen: application terminated with error code 101
修改
鉴于FrancisGagné的评论,我发现Rust实现了在操作期间检查溢出的运算符,例如checked_mul,我发现需要自己实现溢出检查。这是有道理的,因为应该优化发布版本,并且不断检查溢出可能会变得昂贵。所以我不再看到“不一致”。但是,我仍然感到惊讶,分配一个溢出的值不会导致编译时错误。在golang
中,它会:Go Playground
答案 0 :(得分:3)
实际上,您的评论与您观察到的行为不一致:
Go示例类似于第一个Rust示例(除了Go,按设计,没有警告)。
在Rust中,下溢或溢出会导致计算机科学中的未指定值,可以是!
或 bottom ,这是一个表示控件的特殊值流动分歧,通常意味着堕胎或例外。
此规范允许:
并且两种模式都符合规范。
1 默认情况下不进行检测,如果您选择并且在重数字代码之外的相对适度的性能成本,则可以使用简单标志激活Release中的溢出检查。
关于溢出检查的代价:当前的Rust / LLVM情况有助于调试,但尚未真正优化。因此,在此框架中,溢出检查成本。如果情况有所改善,那么有一天,即使在Release中,rustc也可能决定默认激活溢出检查。
在Midori(使用与C#类似的语言开发的Microsoft实验操作系统)中,即使在发布版本中也启用了溢出检查:
在Midori中,我们默认使用溢出检查进行编译。这与库存C#不同,您必须为此行为显式传递/ checked标志。根据我们的经验,捕获和无意中出现的令人惊讶的溢出数量非常值得给您带来不便和成本。但这确实意味着我们的编译器需要非常善于理解如何消除不必要的编译器。
显然,他们改进了编译器,以便:
后者只能在Release中完成(你会失去精确度),但会减少分支数量。
那么,还有什么成本?
阻碍优化的潜在不同的算术规则:
64 + x - 128
可以优化为x - 64
;激活溢出检查后,编译器可能无法执行此优化尽管如此,除非代码是大量数字(例如科学模拟或图形),否则它可能会影响它。