root@coding-prodigies:~# โ–Š
// lesson 2 of 12 ยท 18 min

Syntax and ownership basics

โš‘ Report an issue with this lesson
fn main() {
    let name = String::from("Ada");
    let age = 28;

    println!("{} is {} years old", name, age);
}

Every value in Rust has exactly one owner. When that owner goes out of scope, the value is dropped automatically -- no garbage collector needed, and no manual free() either.

let s1 = String::from("hello");
let s2 = s1; // ownership moves to s2; s1 is no longer valid
// println!("{}", s1); // this would fail to compile

This is different from most languages you may already know. In Python or JavaScript, s2 = s1 just makes a second name for the same object (or copies a primitive). In Rust, if the type doesn't implement Copy (like String, which owns a heap allocation), assignment moves the value -- the old variable becomes invalid, and the compiler will refuse to let you use it again. This isn't a runtime check; it's caught entirely at compile time, which is why Rust programs don't need a garbage collector to know when memory can be freed. Simple stack-only types like i32, f64, and bool implement Copy, so assigning them duplicates the value instead of moving it:

let x = 5;
let y = x; // copies -- x is still valid
println!("{} {}", x, y); // fine: prints "5 5"

Passing a value into a function works the same way as assignment -- ownership moves into the function unless you pass a reference instead. References let you use a value without taking ownership of it, which is usually what you want when a function just needs to read or briefly modify something:

fn print_length(s: &String) {
    println!("length: {}", s.len());
} // s goes out of scope here, but nothing is dropped -- it never owned the data

fn add_exclamation(s: &mut String) {
    s.push_str("!");
}

fn main() {
    let mut name = String::from("Ada");
    print_length(&name);       // borrow immutably -- name is still usable
    add_exclamation(&mut name); // borrow mutably -- allowed to change it
    println!("{}", name);       // "Ada!" -- name is still owned by main
}

The borrow checker enforces one rule that eliminates a huge class of bugs found in C and C++: at any given time you can have either one mutable reference or any number of immutable references to a value, never both at once. That prevents data races and "use after free" style bugs entirely at compile time, before the program ever runs.

Try it yourself

Exercise: Write print_length(s: &String) and call it without taking ownership of name.
Expected output: open-ended โ€” there's no single correct output here, just get your code running without errors.
rust
Output

      
    

Run your code and get it working before marking this lesson complete.

// free preview

9 more lessons โ€” including Capstone project: a course catalog โ€” plus a certificate are waiting.

Unlock the full course โ€” $149.99