Lab 1: Rust Basics
Due February 14, 11:59pm
In this lab, we'll be getting some practice writing basic Rust functions using control flow, as well as learning some tools that will help us write good Rust code for the rest of the semester.
Grading Rubric
- Code is formatted properly using
cargo fmt
: 5% - Code passes
cargo clippy
without warnings: 20% - Code passes our tests: 50%
- Responses in
questionnaire.md
: 25%
Table of Contents
- Setting up your environment: Installing Cargo, cloning git repos, and installing
rust-analyzer
on VSCode. - Writing basic functions: Assignment direction.
- Formatting your code: How to automatically format your code.
- Styling your code: How to check your code for style.
- Testing your code: How to write tests.
- Questionnaire: Answer some brief questions about Rust.
- Feedback: Give us your feedback!
- Submitting: How to submit your lab.
Setting up your environment
Since the lab computer don't currently come with Cargo installed, we'll install it ourselves. If you'd like to work on your personal computer, visit https://rustup.rs/ for installation instructions. This will take up about ~1.2GB on your machine, but you can always uninstall it later.
For the lab machines, you'll need to run the following lines in the terminal:
cd
mkdir /scratch/<your-username>/.rustup
ln -s /scratch/<your-username>/.rustup ~/.rustup
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
Make sure to replace
<your-username>
with your Swarthmore username. For instance, you might writemkdir /scratch/student1/.rustup
if your Swarthmore username were
student1
. (It isn’t.)
Next, create a directory for where you'll store the repositories for this course:
mkdir cs17sr && cd cs17sr
Then, clone your starter code from your Git repo, ensuring to replace <your-username>
with your Swarthmore username as well:
git clone git@github.swarthmore.edu:cs17sr-s23/lab1-<your-username>.git
Running the clone command will cause git
to create a directory named e.g. lab1-student1
in your current directory.
For editing, you should be using VSCode because it has solid integration with rust-analyzer
, a utility that is extremely helpful for writing Rust code.
Run the following command to install it as a VSCode extension:
code --install-extension rust-lang.rust-analyzer
Then you can open your repo with VSCode, making sure to replace student1
with your username:
code lab1-student1
If you've done everything correctly, you should see this file tree on the left side of your VSCode window:
src/
├─ lib.rs
├─ tests.rs
.gitignore
Cargo.toml
questionnaire.md
Now you're ready to get started!
Writing basic functions
In src/lib.rs
, you'll find a variety of functions with comments explaining what it should do, and it's your task to implement them.
Several of these functions can be solved iteratively and recursively, and you should aim to use both of these strategies for at least one function each.
If you're having troubles, that's totally okay! Learning the syntax of a new language is always tricky, so please don't hesitate to ask questions either in person or on Courselore.
Tips
- When you start, each function will each contain a
todo!()
statement, which is a placeholder macro that will allow the program to compile but instantly crashes when the function is called. Make sure to remove this when you implement each function! - Don't be afraid to use
return
when you need to return a value that's not the last expression, such as in the middle of a loop. - Remember to use
let mut
for variables you might want to update. - Read the instructions and error messages carefully.
Formatting your code
Although Rust doesn't care about whitespace in code similar to C++, the Rust community has generally agreed on formatting guidelines so all Rust programs can have some internal consistency with spacing. In fact, Cargo even comes with a built-in command to format your code according to these guidelines automatically:
cargo fmt
Make sure to run this before your submit your code, as it is worth 5% of your lab grade and is so easy to do!
Styling your code
While cargo fmt
can be used to fix up the whitespace on your code, Cargo also comes with a built-in linter called Clippy, which can detect and provide suggestions for patterns that are not idiomatic Rust code.
To use Clippy, run the following Cargo command:
cargo clippy
To demonstrate Clippy, take the following function:
#![allow(unused)] fn main() { fn add(a: i32, b: i32) -> i32 { return a + b; } }
Although this function works as expected, cargo clippy
will fail because it's more idiomatic to write a + b
instead of return a + b;
.
Clippy is a valuable tool for helping you write more idiomatic programs and catch common antipatterns, so you should become accustomed to taking feedback from it.
Make sure your code passes cargo clippy
before submitting your code, as it is worth 20% of your lab grade!
Testing your code
So far, we've seen cargo fmt
and cargo clippy
.
While these tools can make your code look good and Rusty, they don't say much about the correctness of how your code works.
For that, we need testing.
Near the top of lib.rs
, you'll find these lines:
#![allow(unused)] fn main() { #[cfg(test)] mod tests; }
The second line declares a submodule called tests
, whose implementation can be found in src/tests.rs
, while the first line is an attribute.
It tells the compiler to only look at mod tests
when compiling with the test
flag, otherwise ignore it completely.
Making our way over to src/tests.rs
, we find the following at the top of the file:
#![allow(unused)] fn main() { mod tests { use super::*; } }
The super
part says "things defined in the super module" (src/lib.rs
in this case), and the ::*
means "in this module, bring everything into scope".
This means that functions like is_divisible
, defined in src/lib.rs
, can be used here in src/tests.rs
.
If we wanted to call is_divisible
without this use
line, then we would need to specify the fully-qualified path instead: super::is_divisible
, which says "in the super module, I'm taking about the is_divisible
function."
Writing tests is straight forward: just define an ordinary function with no parameters and returning ()
named whatever you want the test to be called.
Then, write #[test]
above it to tell Rust that it's a test, and put your code inside.
Inside the function, you can use the assert!()
macro which will crash your program if a condition is not true.
See the examples in its documentation for ideas on how to use.
When writing tests, you should try to keep each test function small and not test too many different things at once.
That being said, each test can contain more than a single assert!()
statement.
For example, a test function for the Fibonacci sequence might consist of several checks that different values in the sequence are properly calculated by the fib
function:
#![allow(unused)] fn main() { // Dummy function hardcoded to make this test work. Do not copy! fn fib(n: u32) -> u32 { match n { 0 => 0, 1 => 1, 2 => 1, 3 => 2, 4 => 3, 5 => 5, 6 => 8, _ => unreachable!(), } } #[test] fn test_fib() { let answers = [0, 1, 1, 2, 3, 5, 8]; for i in 0..7 { assert!(fib(i) == answers[i]); } } }
To run all your tests, use the following Cargo command:
cargo test
This will run all functions marked as #[test]
and display the results.
For this lab, you're required to write one test for each function, but are welcome to write more if you'd like. Note that 50% of your grade on this lab comes from your code passing our tests (hidden from you), so it's important that you test your code to build confidence that it works properly and handles any edge cases if there are any.
Questionnaire
The last part of this assignment is to fill our several short response questions in questionnaire.md
.
Feedback
Please fill out this short feedback form.
Submitting
Once you're finished, be sure to verify that:
cargo fmt
has been runcargo clippy
passescargo test
passes
Then you can push your changes with the usual: git add .
, git commit -m "your message"
, and git push
.
Congratulations on finishing!