Bitterless Rust

Variables and Types

Creating Variables

In Rust, you create variables with let.

fn main() {
    let x = 42;
    println!("{}", x); // 42
}

The {} in println! gets replaced with the variable's value.

Variables Are Immutable by Default

fn main() {
    let x = 42;
    x = 100; // Compile error!
}

Rust variables are immutable by default. If you want to change them, add mut:

fn main() {
    let mut x = 42;
    x = 100; // OK
    println!("{}", x); // 100
}

Oversimplification warning: This is subtly different from "constants" in other languages, but for now just remember "you can't change it without mut."

Numeric Types: Just Memorize These 3

Rust has tons of numeric types, but you only need these 3:

Type What it is Examples
i32 Regular integer 42, -1, 0
usize Integer for array indices and lengths (0 or greater) 0, 1, 100
f64 Decimal number 3.14, -0.5, 1.0
fn main() {
    let age: i32 = 25;
    let index: usize = 0;
    let pi: f64 = 3.14;

    println!("age: {}, index: {}, pi: {}", age, index, pi);
}

Oversimplification warning: There are actually tons more like i8, i16, i64, u8, u32, f32, etc. But we're ignoring all of them in this tutorial. We'll survive with just i32, usize, and f64.

Rust can often infer the type without you writing it:

fn main() {
    let x = 42;     // inferred as i32
    let y = 3.14;   // inferred as f64
}

You can usually omit types. Only write them when the compiler can't figure it out.

Type Conversion

You can't do arithmetic with mismatched types. Use as to convert:

fn main() {
    let a: i32 = 10;
    let b: f64 = 3.14;

    let result = a as f64 + b;
    println!("{}", result); // 13.14

    let index = a as usize;
    println!("{}", index); // 10
}

bool

Boolean. Either true or false.

fn main() {
    let is_cool = true;
    let is_boring: bool = false;
    println!("{} {}", is_cool, is_boring);
}

char

A single character. Enclosed in single quotes.

fn main() {
    let c = 'A';
    let emoji = '🦀';
    println!("{} {}", c, emoji);
}

You won't use this directly very often. You'll mostly work with strings (String).

String

There are 2 kinds of strings:

Type What it is
&str String literal. Written directly in code. Fixed.
String Dynamic string. Can be modified.
fn main() {
    let s1 = "hello";                   // &str (string literal)
    let s2 = String::from("hello");     // String
    let s3 = "hello".to_string();       // This is also a String

    println!("{} {} {}", s1, s2, s3);
}

Oversimplification warning: Explaining the difference between &str and String precisely would take a while, so we'll skip it. When in doubt, use String::from("..."). Passing a String where &str is expected usually just works.

println! Formatting

fn main() {
    let name = "Rust";
    let version = 2021;

    // Basic
    println!("Hello, {}!", name);

    // Multiple values
    println!("{} edition {}", name, version);

    // Debug display (useful later with structs)
    println!("{:?}", (1, 2, 3));
}

{} for normal display, {:?} for debug display. Debug display will come in handy in later chapters.


Variables and types are done. Next, let's learn functions and control flow.

Back to book