- Day 1: From nvm to rustup
- Day 2: From npm to cargo
- Day 3: Setting up VS Code
- Day 4: Hello World (and your first two WTFs)
- → Day 5: Borrowing & Ownership
- Day 6: Strings, part 1
- Day 7: Syntax and Language, part 1
- Day 8: Language Part 2: From objects and classes to HashMaps and structs
- Day 9: Language Part 3: Class Methods for Rust Structs (+ enums!)
- Day 10: From Mixins to Traits
- Day 11: The Module System
- Day 12: Strings, Part 2
- Day 13: Results & Options
- Day 14: Managing Errors
- Day 15: Closures
- Day 16: Lifetimes, references, and
- Day 17: Arrays, Loops, and Iterators
- Day 18: Async
- Day 19: Starting a large project
- Day 20: CLI Arguments & Logging
- Day 21: Building and Running WebAssembly
- Day 22: Using JSON
- Day 23: Cheating The Borrow Checker
- Day 24: Crates & Tools
The code in this series can be found at vinodotdev/node-to-rust
This guide is not a comprehensive Rust tutorial and assumes a passing knowledge of type systems as they relate to TypeScript. This guide tries to balance technical accuracy with readability and errs on the side of “gets the point across” vs being 100% correct. If you think something needs more definition, send a note on Twitter at @jsoverson, @vinodotdev, or join our Discord channel.
Before we get into strings, we need to talk about ownership. Ownership in Rust is where things start to get complicated. Not because it’s hard to understand, but because Rust’s rules force you to rethink logic and structure that would work fine elsewhere.
Memory safety is more than just making sure your programs don’t crash. It closes the door to a whole class of security vulnerabilities. You’ve heard of SQL injection, right? If you haven’t, it’s a vulnerability that stems from database clients that create SQL statements by concatenating unsanitized user input. Adversaries exploit this vulnerability by passing cleverly crafted input that alters the final query and runs new instructions. Luckily, the attack surface is manageable and it’s 100% preventable. Even still, it remains the most common vulnerability in web applications today. Memory unsafe code is like having harder to find SQL injection vulnerabilities that can pop up anywhere. Memory safety bugs account for the majority of serious vulnerabilities. Eliminating them altogether with no performance impact is an attractive notion.
This guide won’t duplicate existing content when possible. It’s meant to clarify concepts that you have already encountered. Check out these chapters in the Rust book if you’re skimming here and aren’t following along.
- Rust book Ch.3: Common Programming Concepts
- Rust book Ch.4: Understanding Ownership
- Rust by Example: Variable Bindings
- Rust by Example: Primitives
- Rust by Example: Flow control
- Rust by Example: Functions
Variable assignments & mutability
let for variables that can be reassigned and
const for those that can’t. While Rust also has
const for now.
Where you want
let in Rust. Where you’d use
let mut in Rust. The keyword
The Rust counterpart is:
One major difference with Rust is that you can only reassign a variable with a value of the same type. This won’t work:
That said, you can assign a different type to a variable with the same name by using another
Rust’s Borrow Checker
Rust guarantees memory safety by enforcing some basic – albeit strict – rules for how you pass data around, how you “borrow” data and who “owns” data.
Rule #1: Ownership
When you pass a value, the calling code can no longer access that data. It’s given up ownership. Take a look at the code below and the error that occurs when you try to run it
.unwrap()a lot in example code but it’s not something you should use frequently in production code. We’ll go over it in the Result & Option section but the gist is:
.unwrap()assumes a successful operation and panics (dies) otherwise. It’s OK in examples. It’s not OK in your applications unless you are sure an operation can’t fail.
In your IDE or when you try to run this, notice the error message
use of moved value: source. You’ll see that a lot and it’s important to embed its meaning in your brain now.
error[E0382]: use of moved value: `source` | 4 | let source = read_to_string("./README.md").unwrap(); | ------ move occurs because `source` has type `String`, which does not implement the `Copy` trait 5 | let mut files = HashMap::new(); 6 | files.insert("README", source); | ------ value moved here 7 | files.insert("README2", source); | ^^^^^^ value used here after move For more information about this error, try `rustc --explain E0382`.
When we inserted
source into the HashMap the first time, we gave up ownership. If we want to make the above code compile, we have to clone
source the first time we give it away.
You’ll see notes in these error messages when your value “does not implement the
Copytrait”. We’ll get to traits later but the gist of
Copyis for data that can be reliably, trivially copied. Rust will copy those values automatically for you.
Cloneis for potentially expensive copies and you have to do that yourself.
Rule #2: Borrowing
When borrowing data – when you take a reference to data – you can do it immutably an infinite number of times or mutably only once. Typically, you’ll take a reference by prefixing a value with an ampersand (
&). This gives you the ability to pass potentially large chunks of data around without cloning them every time.
Debugformatter. It’s a handy way of outputting data that doesn’t necessarily have a human-readable format.
If we needed to take a mutable reference of our map, we would write it as
let files_ref = &mut files;.
You’ll encounter the following error when you compile the above code.
error[E0499]: cannot borrow `files` as mutable more than once at a time | 9 | let files_ref = &mut files; | ---------- first mutable borrow occurs here 10 | let files_ref2 = &mut files; | ^^^^^^^^^^ second mutable borrow occurs here 11 | 12 | needs_mutable_ref(files_ref); | --------- first borrow later used here For more information about this error, try `rustc --explain E0499`.
The Rust compiler is smart and getting smarter every release, though. If you reorder your borrows so that it can see that one reference will be finished before you use the other, you’ll be OK.
As you’re starting with Rust, you may find many of your errors can be solved by just switching around around the order of your code. Give it a shot before ripping your hair out.
References support session
When you run it you get:
String before function: Hello! String in function: Hello! What a nice day. String after function: Hello! Number before function: 2000 Number in function: 2001 Number after function: 2000 Object before function: Jane Doe Object in function: Samuel Clemens Object after function: Samuel Clemens
Not using references would be like making a deep copy of every
Object every time you pass it to any function. That would be ridiculous, right? Of course it would.
Objects where the value is a reference.”
Ownership is a core, recurring topic in Rust. We needed to dive into it at a high level before we deal with Strings Day 6: Strings, part 1.