0%

Rust关于数据溢出安全

在4个多月之前,也就是19年的7月份。在大佬的推荐下,有幸拿到了今日头条的面试机会。在漫长的面试过程中,头条的面试官态度热情,问的问题兼具广度和深度。再很多问题回答的一塌糊涂的条件下,也让我面试了长达五小时。特别的最后的面试官,态度友好且诚恳,对我的诸多不足之处提出委婉的建议。有这样的面试体验是非常棒让我获益良多,在此表达我的感谢之情。不过本篇文章不是讲述面试经验或者体会的,是讲面试中被反复提到的问题:

Rust数据计算溢出怎么办?

因为面试的是cpp/rust相关的岗位,面试官提出这个问题之后我是很懵的。以为之前完全没考虑过这个问题。Rust号称安全,那么他是如何处理这个问题的呢。当时我的回答是:换用更大的数据类型。其实当时这个回答说出口,就觉得完全不对,毕竟int32之上还有int64,无论用多大的数据类型,计算依旧可能溢出。

我想今天我可以很好的回答这个问题。在c语言中,无符号整数完全不会溢出(overflow),因为数据一旦超过上限,则自动舍弃高位数据。对于有符号数,一旦超过上限,则标准这是ub。Rust号称安全,从设计上就要尽可能的避免ub,对于数据溢出,也算是语言必须面对的bug。rust处理这个bug分为以下两种情况(默认情况,先不谈编译参数的调整)

  1. debug模式下,编译器会自动插入溢出检查,一旦overflow则立即panic。
  2. release模式下,编译器不进行溢出检查,一旦overflow则舍弃高位

如果我们在编译时调整编译参数

rustc -C overflow-checks=no

overflow-checks=yes 或者 no,可以打开或关闭编译器的溢出检查。这样的话,无论是debug还是release模式都可以有统一的溢出检查设置。

当然,我们需要的不仅仅是这个,如果需要更细力度的溢出处理,我们可以使用以下函数

1
2
3
check_*
saturating_*
warpping_*

查阅API文档可知

  1. check_*函数返回Option,一旦发生溢出则返回None
  2. saturating_*系列函数返回类型是整数,如果溢出,则给出该类型可表示范围的“最大/最小”值
  3. wrapping_*系列函数则是直接抛弃已经溢出的最高位,将剩下的部分返回

如果你再进一步仔细查阅Rust源码,发现源码中大量的数据运算都采用这三个函数。当然了,如果你不使用这三类函数,你还可以使用std::num::Warping的类型,他重载了基本运算符。溢出直接截断。这类包裹的用法,不受编译器溢出参数的影响,永远直接截断高位,不会panic.

以上方法就是rust处理溢出的所有情况。