Primitive types and operators

TODO int/uint -> isuze/usize

Rust has pretty much the same arithmetic and logical operators as C++. bool is the same in both languages (as are the true and false literals). Rust has similar concepts of integers, unsigned integers, and floats. However the syntax is a bit different. Rust uses int to mean an integer and uint to mean an unsigned integer. These types are pointer sized. E.g., on a 32 bit system, uint means a 32 bit unsigned integer. Rust also has explicitly sized types which are u or i followed by 8, 16, 32, or 64. So, for example, u8 is an 8 bit unsigned integer and i32 is a 32 bit signed integer. For floats, Rust has f32 and f64.

Numeric literals can take suffixes to indicate their type (using i and u instead of int and uint). If no suffix is given, Rust tries to infer the type. If it can't infer, it uses int or f64 (if there is a decimal point). Examples:

fn main() {
    let x: bool = true;
    let x = 34;   // type int
    let x = 34u;  // type uint
    let x: u8 = 34u8;
    let x = 34i64;
    let x = 34f32;

As a side note, Rust lets you redefine variables so the above code is legal - each let statement creates a new variable x and hides the previous one. This is more useful than you might expect due to variables being immutable by default.

Numeric literals can be given as binary, octal, and hexadecimal, as well as decimal. Use the 0b, 0o, and 0x prefixes, respectively. You can use an underscore anywhere in a numeric literal and it will be ignored. E.g,

fn main() {
    let x = 12;
    let x = 0b1100;
    let x = 0o14;
    let x = 0xe;
    let y = 0b_1100_0011_1011_0001;

Rust has chars and strings, but since they are Unicode, they are a bit different from C++. I'm going to postpone talking about them until after I've introduced pointers, references, and vectors (arrays).

Rust does not implicitly coerce numeric types. In general, Rust has much less implicit coercion and subtyping than C++. Rust uses the as keyword for explicit coercions and casting. Any numeric value can be cast to another numeric type. as cannot be used to convert between booleans and numeric types. E.g.,

fn main() {
    let x = 34u as int;     // cast unsigned int to int
    let x = 10 as f32;      // int to float
    let x = 10.45f64 as i8; // float to int (loses precision)
    let x = 4u8 as u64;     // gains precision
    let x = 400u16 as u8;   // 144, loses precision (and thus changes the value)
    println!("`400u16 as u8` gives {}", x);
    let x = -3i8 as u8;     // 253, signed to unsigned (changes sign)
    println!("`-3i8 as u8` gives {}", x);
    //let x = 45u as bool;  // FAILS!

Rust has the following numeric operators: +, -, *, /, %; bitwise operators: |, &, ^, <<, >>; comparison operators: ==, !=, >, <, >=, <=; short-circuit logical operators: ||, &&. All of these behave as in C++, however, Rust is a bit stricter about the types the operators can be applied to - the bitwise operators can only be applied to integers and the logical operators can only be applied to booleans. Rust has the - unary operator which negates a number. The ! operator negates a boolean and inverts every bit on an integer type (equivalent to ~ in C++ in the latter case). Rust has compound assignment operators as in C++, e.g., +=, but does not have increment or decrement operators (e.g., ++).