Hello Rust!
About This Module
This module provides your first hands-on experience with Rust programming. You'll write actual programs, understand basic syntax, and see how Rust's compilation process works. We'll focus on building confidence through practical programming while comparing key concepts to Python.
Prework
Prework Readings
Review this module.
Read the following Rust basics:
- The Rust Programming Language - Chapter 1.2: Hello, World!
- The Rust Programming Language - Chapter 1.3: Hello, Cargo!
Optionally browse:
Pre-lecture Reflections
Before class, consider these questions:
- How does compiling code differ from running Python scripts directly?
- What might be the advantages of catching errors before your program runs?
- How does Rust's
println!macro compare to Python'sprint()function? - Why might explicit type declarations help prevent bugs?
- What challenges might you face transitioning from Python's flexibility to Rust's strictness?
Topics
- Installing Rust
- Compiled vs Interpretted Languages
- Write and compile our first simple program
Installing Rust
Before we can write Rust programs, we need to install Rust on your system.
From https://www.rust-lang.org/tools/install:
On MacOS:
# Install Rust via rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Question: can you interpret the shell command above?
On Windows:
Download and run rustup-init.exe (64-bit).
It will ask you some questions.
Download Visual Studio Community Edition Installer.
Open up Visual Studio Community Edition Installer and install the C++ core
desktop features.
Verify Installation
From MacOS terminal or Windows CMD or PowerShell
rustc --version # Should show Rust compiler version
cargo --version # Should show Cargo package manager version
rustup --version # Should show Rustup toolchain installer version
Troubleshooting Installation:
# Update Rust if already installed
rustup update
# Check which toolchain is active
rustup show
# Reinstall if needed (a last resort!!)
rustup self uninstall
# Then reinstall following installation steps above
Write and compile simple Rust program
Generally you would create a project directory for all your projects and then a subdirectory for each project.
Follow along now if you have Rust installed, or try at your first opportunity later.
$ mkdir ~/projects
$ cd ~/projects
$ mkdir hello_world
$ cd hello_world
All Rust source files have the extension .rs.
Create and edit a file called main.rs.
For example with the nano editor on MacOS
# From MacoS terminal
nano main.rs
or notepad on Windows
# From Windows CMD or PowerShell
notepad main.rs
and add the following code:
fn main() { println!("Hello, world!"); }
Note: Since our course notes are in
mdbook, code cells like above can be executed right from the notes!In many cases we make the code cell editable right on the web page!
If you created that file on the command line, then you compile and run the program with the following commands:
$ rustc main.rs # compile with rustc which creates an executable
If it compiled correctly, you should have a new file in your directory
For example on MacOS or Linux you might see:
hello_world % ls -l
total 880
-rwxr-xr-x 1 tgardos staff 446280 Sep 10 21:03 main
-rw-r--r-- 1 tgardos staff 45 Sep 10 21:02 main.rs
Question: What is the new file? What do you observe about the file properties?
On Windows you'll see main.exe.
$ ./main # run the executable
Hello, world!
Compiled (e.g. Rust) vs. Interpreted (e.g. Python)
Python: One Step (Interpreted)
python hello.py
- Python reads your code line by line and executes it immediately
- No separate compilation step needed
Rust: Two Steps (Compiled)
# Step 1: Compile (translate to machine code)
rustc hello.rs
# Step 2: Run the executable
./hello
rustcis your compilerrustctranslates your entire program to machine code- Then you run the executable (why
./?)
The main() function
fn main() { ... }
is how you define a function in Rust.
The function name main is reserved and is the entry point of the program.
The println!() Macro
Let's look at the single line of code in the main function:
println!("Hello, world!");
Rust convention is to indent with 4 spaces -- never use tabs!!
println!is a macro which is indicated by the!suffix.- Macros are functions that are expanded at compile time.
- The string
"Hello, world!"is passed as an argument to the macro.
The line ends with a ; which is the end of the statement.
More Printing Tricks
Let's look at a program that prints in a bunch of different ways.
// A bunch of the output routines fn main() { let x = 9; let y = 16; print!("Hello, DS210!\n"); // Need to include the newline character println!("Hello, DS210!\n"); // The newline character here is redundant println!("{} plus {} is {}", x, y, x+y); // print with formatting placeholders //println!("{x} plus {y} is {x+y}"); // error: cannot use `x+y` in a format string println!("{x} plus {y} is {}\n", x+y); // but you can put variable names in the format string }
More on println!
- first parameter is a format string
{}are replaced by the following parameters
print! is similar to println! but does not add a newline at the end.
To dig deeper on formatting strings:
fmtmodule- Format strings syntax
Input Routines
Here's a fancier program. You don't have to worry about the details, but
paste it into a file name.rs, run rustc name.rs and then ./name.
// And some input routines
// So this is for demo purposes
use std::io;
use std::io::Write;
fn main() {
let mut user_input = String::new();
print!("What's your name? ");
io::stdout().flush().expect("Error flushing"); // flush the output and print error if it fails
let _ =io::stdin().read_line(&mut user_input); // read the input and store it in user_input
println!("Hello, {}!", user_input.trim());
}
Project manager: cargo
Rust comes with a very helpful project and package manager: cargo
-
create a project:
cargo new PROJECT-NAME -
main file will be
PROJECT-NAME/src/main.rs -
to run:
cargo run -
to just build:
cargo build
Cargo example
~ % cd ~/projects
projects % cargo new cargo-hello
Creating binary (application) `cargo-hello` package
note: see more `Cargo.toml` keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
projects % cd cargo-hello
cargo-hello % tree
.
├── Cargo.toml
└── src
└── main.rs
2 directories, 2 files
cargo-hello % cargo run
Compiling cargo-hello v0.1.0 (/Users/tgardos/projects/cargo-hello)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.21s
Running `target/debug/cargo-hello`
Hello, world!
% tree -L 3
.
├── Cargo.lock
├── Cargo.toml
├── src
│ └── main.rs
└── target
├── CACHEDIR.TAG
└── debug
├── build
├── cargo-hello
├── cargo-hello.d
├── deps
├── examples
└── incremental
8 directories, 6 files
Cargo --release
By default, cargo makes a slower debug build that has extra
debugging information.
We'll see more about that later.
Add --release to create a "fully optimized" version:
- longer compilation
- faster execution
- some runtime checks not included (e.g., integer overflow)
- debuging information not included
- the executable in a different folder
cargo-hello (master) % cargo build --release
Compiling cargo-hello v0.1.0 (/Users/tgardos/projects/cargo-hello)
Finished `release` profile [optimized] target(s) in 0.38s
(.venv) √ cargo-hello (master) % tree -L 2
.
├── Cargo.lock
├── Cargo.toml
├── src
│ └── main.rs
└── target
├── CACHEDIR.TAG
├── debug
└── release
5 directories, 4 files
Cargo check
If you just want to check if your current version compiles: cargo check
- Much faster for big projects
Hello Rust Activity
-
Get in groups of 3+
-
Place the lines of code in order in two parts on the page: your shell, and your code file
main.rsto make a reasonable sequence and functional code. -
We'll take the last 5 minutes to share solutions
-
Don't stress if you didn't finish! Just paste what you have into GradeScope.
println!("Good work! Average: {:.1}", average);
cargo run
scores.push(88);
git push -u origin main
let average = total as f64 / scores.len() as f64;
cargo new hello_world
} else if average >= 80.0 {
nano src/main.rs
let total: i32 = scores.iter().sum();
if average >= 90.0 {
touch README.md
cd hello_world
fn main() {
git add src/main.rs
println!("Keep trying! Average: {:.1}", average);
let mut scores = vec![85, 92, 78, 96];
ls -la
echo "This is a grade average calculator" > README.md
} else {
git commit -m "Add calculator functionality"
}}
println!("Excellent! Average: {:.1}", average);