{"version":3,"sources":["webpack:///path---blog-rust-overflow-94117416bd98b202d62e.js","webpack:///./.cache/json/blog-rust-overflow.json"],"names":["webpackJsonp","366","module","exports","data","site","siteMetadata","title","author","markdownRemark","id","html","frontmatter","date","pathContext","slug","previous","fields"],"mappings":"AAAAA,cAAc,iBAERC,IACA,SAAUC,EAAQC,GCHxBD,EAAAC,SAAkBC,MAAQC,MAAQC,cAAgBC,MAAA,eAAAC,OAAA,kBAAiDC,gBAAmBC,GAAA,4GAAAC,KAAA;AAAo8mCC,aAAgqBL,MAAA,oCAAAM,KAAA,oBAAsEC,aAAgBC,KAAA,uBAAAC,UAA0CC,QAAUF,KAAA,4BAAkCH,aAAgBL,MAAA","file":"path---blog-rust-overflow-94117416bd98b202d62e.js","sourcesContent":["webpackJsonp([40042798607685],{\n\n/***/ 366:\n/***/ (function(module, exports) {\n\n\tmodule.exports = {\"data\":{\"site\":{\"siteMetadata\":{\"title\":\"Valencik.com\",\"author\":\"K.J. Valencik\"}},\"markdownRemark\":{\"id\":\"/home/kvalencik/git/valencik.com/src/pages/blog/rust-overflow/index.md absPath of file >>> MarkdownRemark\",\"html\":\"
One of the core principles of Rust is borrowed from C++—zero-cost abstractions.
\\n\\n\\nIn general, C++ implementations obey the zero-overhead principle: What you\\ndon’t use, you don’t pay for. And further: What you do use, you couldn’t hand\\ncode any better.
\\n— Bjarne Stroustrup
\\n
This permeates much of the ecosystem including traits,\\nborrowing, and iterators. One of the more interesting\\ntrade-offs is the way integer overflow is handled. Fixed size integers in Rust\\nare implemented with two’s complement and may\\noverflow.
\\nFor example, assume with have a u8
, an unsigned 8-bit integer. It can\\nrepresent values from 0
to 255
.
fn main() {\\n\\tlet x = 254u8;\\n\\tprintln!(\\\"{} {:b}\\\", x, x);\\n\\n\\tlet y = x + 1;\\n\\tprintln!(\\\"{} {:b}\\\", y, y);\\n\\n\\tlet z = y + 1;\\n\\tprintln!(\\\"{} {:b}\\\", z, z);\\n}\\n
\\n After performing the second addition, the correct result should be 256
, but\\nthis exceeds the maximum value of a u8
.
Note: If you are following along, all examples use\\ncargo-script.
\\n$ cargo script --debug main.rs\\n254 11111110\\n255 11111111\\nthread 'main' panicked at 'attempt to add with overflow', main.rs:8:10
\\n Rust detects the overflow and panics, unwinding the stack and exiting the\\nprocess. Unfortunately, the process of checking arithmetic for overflow is very\\nexpensive. Rust makes a trade-off for performance by disabling overflow checks\\nin release mode.
\\n$ cargo script main.rs\\n254 11111110\\n255 11111111\\n0 0
\\n In this example, instead of a panic the result wrapped around by truncating the\\nhighest bits. By making this trade-off, hopefully, overflow bugs are caught in\\ntesting without impacting release performance. The specifics of integer overflow\\nin Rust are defined by RFC 560.
\\nDiverging from Rust for a moment, let’s discuss some math. Radians are\\na unit for measuring angles. Radians are defined as the ratio of arc length to\\nthe radius of a circle. As such, a full turn is equivalent to 2π rad.
\\n\\n2π rad=360°\\nSince angles are periodic by nature, the range of possible values is limited\\nsuch that for any angle α
\\n\\n0≤α <2π\\nLet’s define a new unit of measure for angles. We define the unit as a fraction\\nof a radian.
\\n\\n2π rad=65536 unit0 unit=65536 unit\\nGreat! We’ve defined a new unit and we can convert to and from an SI\\nunit. Now what?
\\nA keen eye will have noticed the power of 2. If we map our angles to discrete\\nunits, the maximum value is the same as an unsigned 16-bit integer (u16
).
This is a very neat property. We have mapped the periodic nature of angles to\\nthe overflow mechanics of two’s compliment arithmetic. This allows for some\\noptimizations. For example, comparing two angles for equality.
\\nTwo angles α and β are equal, α=β, if and only if\\nthere exists some x∈Z such that x⋅α=β or\\nα=x⋅β.
\\nTypically this requires division and comparing against some ϵ error\\nmargin in the case of floats. Instead, we can simply compare their values.
\\nfn main() {\\n\\tlet x = 0u16;\\n\\tlet y = x + 65535 + 1;\\n\\n\\tprintln!(\\\"{}\\\", x == y);\\n}\\n
\\n $ cargo script --debug main.rs\\nthread 'main' panicked at 'attempt to add with overflow', main.rs:3:10
\\n Uh, oh. That’s not what we wanted.
\\nRust is preventing us from performing exactly the operation we want—addition\\nwith overflow. For precisely this purpose, Rust provides wrapping_
methods.\\nThe wrapping_add
method on u16
will sum the two numbers with\\nwrapping on overflow.
fn main() {\\n\\tlet x = 0u16;\\n\\tlet y = x.wrapping_add(65535).wrapping_add(1);\\n\\n\\tprintln!(\\\"{}\\\", x == y);\\n}\\n
\\n $ cargo script --debug main.rs\\ntrue
\\n Great! Our code is working again, but we’ve lost the ergonomics of code that\\nlooks similar to the mathematical operations we want to perform. Rust provides\\noperator overloading via the std::ops
traits. We can follow the\\nnewtype idiom and implement the std::ops::Add
trait on\\nthat.
use std::ops::Add;\\n\\n#[derive(Clone, Copy, PartialEq)]\\nstruct Angle(u16);\\n\\nimpl Add for Angle {\\n\\ttype Output = Angle;\\n\\n\\tfn add(self, rhs: Self::Output) -> Self::Output {\\n\\t\\tAngle(self.0.wrapping_add(rhs.0))\\n\\t}\\n}\\n\\nfn main() {\\n\\tlet x = Angle(0u16);\\n\\tlet y = x + Angle(65535) + Angle(1);\\n\\n\\tprintln!(\\\"{}\\\", x == y);\\n}\\n
\\n In addition to providing a struct to impl, the newtype pattern is useful for\\nensuring correctness. For example, the compiler prevents us from accidentally\\nadding our Angle
type to a simple u16
which may represent a different\\nscale.
fn main() {\\n\\tlet x = Angle(0u16);\\n\\tlet y = 65535u16; // Represents 180 deg instead of 360 deg\\n\\tlet z = x + y + Angle(1);\\n\\n\\tprintln!(\\\"{}\\\", x == z);\\n}\\n
\\n error[E0308]: mismatched types\\n --> main.rs:17:14\\n |\\n17 | let z = x + y + Angle(1);\\n | ^\\n | |\\n | expected struct `Angle`, found u16\\n | help: try using a variant of the expected type: `Angle(y)`\\n |\\n = note: expected type `Angle`\\n found type `u16`
\\n We likely need to support other arithmetic operations besides addition. It can\\nget tedious to implement each of these traits on Angle
. Fortunately, this was\\nconsidered as part of the RFC. std::num::Wrapping<T>
is a generic\\nstruct with specialized implementation of many arithmetic traits.
We can replace our Angle
struct with Wrapping
and get operator overloading\\nwith wrapping on overflow for free!
use std::num::Wrapping;\\n\\nfn main() {\\n\\tlet x = Wrapping(0u16)\\n\\tlet y = Wrapping(65535u16);\\n\\tlet z = x - Wrapping(1);\\n\\n\\tprintln!(\\\"{}\\\", y == z);\\n}\\n
\\n $ cargo script --debug main.rs\\ntrue
\\n When switching from the Angle
newtype to the built-in Wrapping
struct we\\ntraded the type checking validation of our angle units to eliminate the\\nboilerplate of lots of traits impls. This isn’t an ideal trade-off.
It would be great if Rust included something analogous to Haskell’s\\nGeneralisedNewtypeDeriving
. This extension allows\\nderiving any traits implemented by the enclosed type. At the time of this\\nwriting, Rust does not have anything this powerful. But, the macro system can\\ncome close for this use case.
The derive_more crate provides procedural\\nmacros for deriving common traits by inferring an\\nimplementation from the shape of the data. This works fairly well for simple\\ntypes.
\\n//! ```cargo\\n//! [dependencies]\\n//! derive_more = \\\"0.7\\\"\\n//! ```\\n\\n#[macro_use]\\nextern crate derive_more;\\n\\nuse std::num::Wrapping;\\n\\n// Use the `derive_more` crate to derive `Add` and `Sub`!\\n#[derive(PartialEq, Clone, Copy, Add, Sub)]\\nstruct Angle(Wrapping<u16>);\\n\\n// Implement `From` for our newtype to create an `Angle` directly from a `u16`\\nimpl From<u16> for Angle {\\n\\tfn from(n: u16) -> Self {\\n\\t\\tAngle(Wrapping(n))\\n\\t}\\n}\\n\\nfn main() {\\n\\tlet x = Angle::from(0);\\n\\tlet y = Angle::from(65535);\\n\\n\\t// The `From` trait provides `into`. The type can be inferred.\\n\\tlet z = x + x - 1.into();\\n\\n\\tprintln!(\\\"{}\\\", y == z);\\n}\\n
\\n $ cargo script --debug main.rs\\ntrue
\\n We wrap the Wrapping
struct in our newtype and use derive_more
to derive an\\nimpl
for Sub
on Angle
. We manually implement From
to make it easier to\\nget a new Angle
directly from a u16
.
I enjoyed delving into the various abstractions that rust provides and the\\ndecisions and trade-offs that language makes. I hope you learned something\\ninteresting as well!
\",\"frontmatter\":{\"title\":\"Radians and Rust Integer Overflow\",\"date\":\"March 08, 2018\"}}},\"pathContext\":{\"slug\":\"/blog/rust-overflow/\",\"previous\":{\"fields\":{\"slug\":\"/blog/extending-promise/\"},\"frontmatter\":{\"title\":\"Extending Promise\"}}}}\n\n/***/ })\n\n});\n\n\n// WEBPACK FOOTER //\n// path---blog-rust-overflow-94117416bd98b202d62e.js","module.exports = {\"data\":{\"site\":{\"siteMetadata\":{\"title\":\"Valencik.com\",\"author\":\"K.J. Valencik\"}},\"markdownRemark\":{\"id\":\"/home/kvalencik/git/valencik.com/src/pages/blog/rust-overflow/index.md absPath of file >>> MarkdownRemark\",\"html\":\"One of the core principles of Rust is borrowed from C++—zero-cost abstractions.
\\n\\n\\nIn general, C++ implementations obey the zero-overhead principle: What you\\ndon’t use, you don’t pay for. And further: What you do use, you couldn’t hand\\ncode any better.
\\n— Bjarne Stroustrup
\\n
This permeates much of the ecosystem including traits,\\nborrowing, and iterators. One of the more interesting\\ntrade-offs is the way integer overflow is handled. Fixed size integers in Rust\\nare implemented with two’s complement and may\\noverflow.
\\nFor example, assume with have a u8
, an unsigned 8-bit integer. It can\\nrepresent values from 0
to 255
.
fn main() {\\n\\tlet x = 254u8;\\n\\tprintln!(\\\"{} {:b}\\\", x, x);\\n\\n\\tlet y = x + 1;\\n\\tprintln!(\\\"{} {:b}\\\", y, y);\\n\\n\\tlet z = y + 1;\\n\\tprintln!(\\\"{} {:b}\\\", z, z);\\n}\\n
\\n After performing the second addition, the correct result should be 256
, but\\nthis exceeds the maximum value of a u8
.
Note: If you are following along, all examples use\\ncargo-script.
\\n$ cargo script --debug main.rs\\n254 11111110\\n255 11111111\\nthread 'main' panicked at 'attempt to add with overflow', main.rs:8:10
\\n Rust detects the overflow and panics, unwinding the stack and exiting the\\nprocess. Unfortunately, the process of checking arithmetic for overflow is very\\nexpensive. Rust makes a trade-off for performance by disabling overflow checks\\nin release mode.
\\n$ cargo script main.rs\\n254 11111110\\n255 11111111\\n0 0
\\n In this example, instead of a panic the result wrapped around by truncating the\\nhighest bits. By making this trade-off, hopefully, overflow bugs are caught in\\ntesting without impacting release performance. The specifics of integer overflow\\nin Rust are defined by RFC 560.
\\nDiverging from Rust for a moment, let’s discuss some math. Radians are\\na unit for measuring angles. Radians are defined as the ratio of arc length to\\nthe radius of a circle. As such, a full turn is equivalent to 2π rad.
\\n\\n2π rad=360°\\nSince angles are periodic by nature, the range of possible values is limited\\nsuch that for any angle α
\\n\\n0≤α <2π\\nLet’s define a new unit of measure for angles. We define the unit as a fraction\\nof a radian.
\\n\\n2π rad=65536 unit0 unit=65536 unit\\nGreat! We’ve defined a new unit and we can convert to and from an SI\\nunit. Now what?
\\nA keen eye will have noticed the power of 2. If we map our angles to discrete\\nunits, the maximum value is the same as an unsigned 16-bit integer (u16
).
This is a very neat property. We have mapped the periodic nature of angles to\\nthe overflow mechanics of two’s compliment arithmetic. This allows for some\\noptimizations. For example, comparing two angles for equality.
\\nTwo angles α and β are equal, α=β, if and only if\\nthere exists some x∈Z such that x⋅α=β or\\nα=x⋅β.
\\nTypically this requires division and comparing against some ϵ error\\nmargin in the case of floats. Instead, we can simply compare their values.
\\nfn main() {\\n\\tlet x = 0u16;\\n\\tlet y = x + 65535 + 1;\\n\\n\\tprintln!(\\\"{}\\\", x == y);\\n}\\n
\\n $ cargo script --debug main.rs\\nthread 'main' panicked at 'attempt to add with overflow', main.rs:3:10
\\n Uh, oh. That’s not what we wanted.
\\nRust is preventing us from performing exactly the operation we want—addition\\nwith overflow. For precisely this purpose, Rust provides wrapping_
methods.\\nThe wrapping_add
method on u16
will sum the two numbers with\\nwrapping on overflow.
fn main() {\\n\\tlet x = 0u16;\\n\\tlet y = x.wrapping_add(65535).wrapping_add(1);\\n\\n\\tprintln!(\\\"{}\\\", x == y);\\n}\\n
\\n $ cargo script --debug main.rs\\ntrue
\\n Great! Our code is working again, but we’ve lost the ergonomics of code that\\nlooks similar to the mathematical operations we want to perform. Rust provides\\noperator overloading via the std::ops
traits. We can follow the\\nnewtype idiom and implement the std::ops::Add
trait on\\nthat.
use std::ops::Add;\\n\\n#[derive(Clone, Copy, PartialEq)]\\nstruct Angle(u16);\\n\\nimpl Add for Angle {\\n\\ttype Output = Angle;\\n\\n\\tfn add(self, rhs: Self::Output) -> Self::Output {\\n\\t\\tAngle(self.0.wrapping_add(rhs.0))\\n\\t}\\n}\\n\\nfn main() {\\n\\tlet x = Angle(0u16);\\n\\tlet y = x + Angle(65535) + Angle(1);\\n\\n\\tprintln!(\\\"{}\\\", x == y);\\n}\\n
\\n In addition to providing a struct to impl, the newtype pattern is useful for\\nensuring correctness. For example, the compiler prevents us from accidentally\\nadding our Angle
type to a simple u16
which may represent a different\\nscale.
fn main() {\\n\\tlet x = Angle(0u16);\\n\\tlet y = 65535u16; // Represents 180 deg instead of 360 deg\\n\\tlet z = x + y + Angle(1);\\n\\n\\tprintln!(\\\"{}\\\", x == z);\\n}\\n
\\n error[E0308]: mismatched types\\n --> main.rs:17:14\\n |\\n17 | let z = x + y + Angle(1);\\n | ^\\n | |\\n | expected struct `Angle`, found u16\\n | help: try using a variant of the expected type: `Angle(y)`\\n |\\n = note: expected type `Angle`\\n found type `u16`
\\n We likely need to support other arithmetic operations besides addition. It can\\nget tedious to implement each of these traits on Angle
. Fortunately, this was\\nconsidered as part of the RFC. std::num::Wrapping<T>
is a generic\\nstruct with specialized implementation of many arithmetic traits.
We can replace our Angle
struct with Wrapping
and get operator overloading\\nwith wrapping on overflow for free!
use std::num::Wrapping;\\n\\nfn main() {\\n\\tlet x = Wrapping(0u16)\\n\\tlet y = Wrapping(65535u16);\\n\\tlet z = x - Wrapping(1);\\n\\n\\tprintln!(\\\"{}\\\", y == z);\\n}\\n
\\n $ cargo script --debug main.rs\\ntrue
\\n When switching from the Angle
newtype to the built-in Wrapping
struct we\\ntraded the type checking validation of our angle units to eliminate the\\nboilerplate of lots of traits impls. This isn’t an ideal trade-off.
It would be great if Rust included something analogous to Haskell’s\\nGeneralisedNewtypeDeriving
. This extension allows\\nderiving any traits implemented by the enclosed type. At the time of this\\nwriting, Rust does not have anything this powerful. But, the macro system can\\ncome close for this use case.
The derive_more crate provides procedural\\nmacros for deriving common traits by inferring an\\nimplementation from the shape of the data. This works fairly well for simple\\ntypes.
\\n//! ```cargo\\n//! [dependencies]\\n//! derive_more = \\\"0.7\\\"\\n//! ```\\n\\n#[macro_use]\\nextern crate derive_more;\\n\\nuse std::num::Wrapping;\\n\\n// Use the `derive_more` crate to derive `Add` and `Sub`!\\n#[derive(PartialEq, Clone, Copy, Add, Sub)]\\nstruct Angle(Wrapping<u16>);\\n\\n// Implement `From` for our newtype to create an `Angle` directly from a `u16`\\nimpl From<u16> for Angle {\\n\\tfn from(n: u16) -> Self {\\n\\t\\tAngle(Wrapping(n))\\n\\t}\\n}\\n\\nfn main() {\\n\\tlet x = Angle::from(0);\\n\\tlet y = Angle::from(65535);\\n\\n\\t// The `From` trait provides `into`. The type can be inferred.\\n\\tlet z = x + x - 1.into();\\n\\n\\tprintln!(\\\"{}\\\", y == z);\\n}\\n
\\n $ cargo script --debug main.rs\\ntrue
\\n We wrap the Wrapping
struct in our newtype and use derive_more
to derive an\\nimpl
for Sub
on Angle
. We manually implement From
to make it easier to\\nget a new Angle
directly from a u16
.
I enjoyed delving into the various abstractions that rust provides and the\\ndecisions and trade-offs that language makes. I hope you learned something\\ninteresting as well!
\",\"frontmatter\":{\"title\":\"Radians and Rust Integer Overflow\",\"date\":\"March 08, 2018\"}}},\"pathContext\":{\"slug\":\"/blog/rust-overflow/\",\"previous\":{\"fields\":{\"slug\":\"/blog/extending-promise/\"},\"frontmatter\":{\"title\":\"Extending Promise\"}}}}\n\n\n//////////////////\n// WEBPACK FOOTER\n// ./~/json-loader!./.cache/json/blog-rust-overflow.json\n// module id = 366\n// module chunks = 40042798607685"],"sourceRoot":""}