Packages

Systems in Rust

Prof. Calvin

Announcements

  • The second homework, “Hi cargo”, is ready after this class.
    • It is comically trivial.
    • Mostly makes sure you have everything set up.
  • Due Friday, 12 Sept. at 1440 ET.

Today

  • Hybrid Day
  • Build system/package manager
    • Theory behind Cargo
    • Trap in Cargo
    • Use Cargo

Build Systems

Interpretation

  • We once again address interpretation vs. compilation
    • I think of this as programming vs. scripting
    • We can think in terms of languages - Rust/C vs. Python/JavaScript

Scripts

  • I regard a script a:
    • Text file
    • Which describes actions for a computer
    • Readable to humans
    • And readable to a program (like python) which can direct the computer to act
    what_up_earth.py
    print("what up earth")
  • We note that this is much closer to being a text file that it is to being a program.

Programs

  • We can create programs but we also use existing programs.
  • As an example, consider the Firefox web-browser
    • If you use Chrome, keep that to yourself, that’s embarassing.
  • This is what “Firefox.exe” looks like on a Window device.
Firefox.exe
A{Ÿ@€ÿw.·þ9ÏCñ~¿‰ó€Ë @€ÿ¶ûCþD8ßué¬%  Aƒù/„î%  L‹RHƒÂHƒÀM…Ò…wÿÿÿM…À„à  ID$¹ÿ   A·ƒú-…“  IPMHfAƒx-IDÑD·fE…À„|  E·ÈA9ÉDCÁEH¿E‰ÂA€Ê A€ùE¶ÊECÈA€ùc…P  D·BfE…À„A  E·ÈA9ÉDCÁEH¿E‰ÂA€Ê A€ùE¶ÊECÈA€ùo…  D·BfE…À„  E·ÈA9ÉDCÁEH¿E‰ÂA€Ê A€ùE¶ÊECÈA€ùn…Ú  D·BfE…À„Ë  E·ÈA9ÉDCÁEH¿E‰ÂA€Ê A€ùE¶ÊECÈA€ùt…Ÿ  D·BfE…À„  E·ÈA9ÉDCÁEH¿E‰ÂA€Ê A€ùE¶ÊECÈA€ùe…d  D·B
fE…À„U  E·ÈA9ÉDCÁEH¿E‰ÂA€Ê A€ùE¶ÊECÈA€ùn…)  D·BfE…À„  E·ÈA9ÉDCÁEH¿E‰ÂA€Ê A€ùE¶ÊECÈA€ùt…î   D·BfE…À„ß   E·ÈA9ÉDCÁEH¿E‰ÂA€Ê A€ùE¶ÊECÈA€ùp…³   D·BfE…À„¤   E·ÈA9ÉDCÁEH¿E‰ÂA€Ê A€ùE¶ÊECÈA€ùru|D·BfE…ÀtqE·ÈA9ÉDCÁEH¿E‰ÂA€Ê A€ùE¶ÊECÈA€ùouID·BfE…Àt>E·ÈA9ÉDCÁEH¿E‰ÂA€Ê A€ùE¶ÊECÈA€ùcufƒz „*  ë   ƒú/„i#  L‹ HƒÀM…À…GýÿÿHŒ$ð  è)˜  fïÀL„$   fA@ fA@fA H„$à  H‰D$ HÇÁÿÿÿÿ1ÒA¹0   èE …Àˆ#  L‹„$H  ¹   1Òÿû H‰ÃH…À„;#  L´$@  AÇFà    AÆFø LŒ$à  AÇ     H‰Ùº   M‰ðÿõú L‰ÿ…À„4#  €¼$8   …_#  D‹„$   L¼$0  L‰ùL‰òè™  A€ …X#  L´$   AÇFà   AÆFø 1ÉL‰òA¸   ÿ0ù =    t…À…!  ÿù ·èÍ  €…ÀNèA¼)   @¶L=t˜ H‰ØHÿÀHƒør    H‰Ùÿ°÷ @„ö…*"  A€÷³1íH
¤ ÿ’ü H…ÀL‹d$`…6#  L‹M…À„w  ID$¹ÿ   A·ƒú-tƒú/„»!  L‹ HƒÀM…ÀuâéJ  IPMHfAƒx-IDÑD·fE…ÀtÓE·ÈA9ÉDCÁEH¿E‰ÂA€Ê A€ùE¶ÊECÈA€ùlu«D·BfE…Àt E·ÈAºÿ   Aùÿ   EBÐEB¿E‰ÑA€É A€øE¶ÁECÂA€øa…jÿÿÿD·BfE…À„[ÿÿÿE·ÈAºÿ   Aùÿ   EBÐEB¿E‰ÑA€É A€øE¶ÁECÂA€øu…%ÿÿÿD·BfE…À„ÿÿÿE·ÈAºÿ   Aùÿ   EBÐEB¿E‰ÑA€É A€øE¶ÁECÂA€øn…àþÿÿD·BfE…À„ÑþÿÿE·ÈAºÿ   Aùÿ   EBÐEB¿E‰ÑA€É A€øE¶ÁECÂA€øc…›þÿÿD·B
fE…À„ŒþÿÿE·ÈAºÿ   Aùÿ   EBÐEB¿E‰ÑA€É A€øE¶ÁECÂA€øh…VþÿÿD·BfE…À„GþÿÿE·ÈAºÿ   Aùÿ   EBÐEB¿E‰ÑA€É A€øE¶ÁECÂA€øe…þÿÿD·BfE…À„þÿÿE·ÈAºÿ   Aùÿ   EBÐEB¿E‰ÑA€É A€øE¶ÁECÂA€ør…Ìýÿÿfƒz …ÁýÿÿH‹H‰HøHƒÀH…ÉuðAÿͰL‹d$`ë1À„Û„Æ   AÇE1ÉAöÇtyL‹M…ÀtqID$¹ÿ   A·ƒú-uIIPMPfAƒx-IDÒD·fE…Àt6E·ÐA9ÊDCÁEP¿E‰ÃA€Ë A€úE¶ÓECÐA€úfuéj   ƒú/„$  L‹ HƒÀM…Àu™Aö×E¶ÇAƒàHŒ$ð  Hœ$   H‰Úè˜  @Šs@„ö…à#  ƒ¼$    I‰ÿu1íë/H
}  ÿ$ù H…À…à%  H
  • I would not regard that as readable to humans (depends on the human)

Compilation

  • Compilation is the process that takes text-based code to whatever we saw on the last slide.
  • Can be quite simple - we made hi_world, a program, already.
    • Notably it didn’t do a lot.
  • rustc takes a .rs file and makes an executable
    • a program, sort of
  • Executables run as command with ./ prefix
    • This differs from python3 which runs a script without creating a corresponding program.

.py vs .rs

flowchart LR
  A(Python) --> B[fname.py file]
  B --> C[python fname.py]
  C --> D{hello world}
  E(Rust) --> F[fname.rs  file]
  F --> G[rustc fname.rs]
  G --> H[./fname]
  H --> I{hello world}

  • You only have to compile once to have the executable forever.
  • Most programs are executables, not scripts.

Rustc

  • rustc is rarely, if ever, used directly
    • Over the years (since like, 1960) code has increased in complexity.
      • Many files (think pgl.py, WordleGraphics.py, EnigmaRotor.py)
      • Many libraries (think NumPy, pandas, scikit)
      • Complex file systems (01/rustup.md)
  • The solution was automation.

Build Systems

  • We don’t have a real equivalence to compilation for Python.
  • We do have a build system equivalent.
    • I use flit, which is easy.
    • I think there’s others… Setuptools? Hatchling?

Flit

  • Flit solves a novel problem for Python programers.
    • Not: Let’s make some code that I can run.
    • Instead: Let’s make code that can be used in code other people run.

Packaging

  • NumPy, pandas, and things like CS 151 PGL can be “built” into packages with Flit and friends.
    • Basically, instead of running e.g. python3 mycode.py
    • Run flit init to make a new folder, edit some code, then run flit publish to add it to pip
  • Takeaway - package management and build systems work real well together.

Wheels

  • The thing that flit and friends create and distribute is not a .py file.
  • Rather, they are “wheels” which are not human readable.
  • A lot like compiled code
  • Takeaway - a good way to think of Rust is that we’re writing packages for ourselves.

Cargo

  • With Python, we use python3 (or pip, but perhaps as python3 -m pip).
  • rustc is not really the equivalent to that:
  • Rather, we use cargo, the Rust build and package manager.
  • cargo will handle any usage of rustc that we may need.
    • We can easily then distribute code across multiple files.

Cargo New

Build+Package

  • Cargo is Rust’s build system (like flit) and package manager (like pip).
  • We will use Cargo instead of rustc for the rest of the term.
    • My sense is that this is the industry standard.
  • We’ll step through an example, then you’ll do a (very simple) exercise.

New

  • The first thing to do with Cargo, as a rule, is great a new package.
    • This is kinda like creating a new .py file.
    • It is a bit more like creating a Python project from a template repository.

Do it

  • To do so:
cargo new throwaway_example
  • cargo is, well, Cargo
  • new creates a new package.
  • throwaway_example is the name of the package1.

Examine

  • The most natural thing is to see what has changed.
ls
  • For me, I see a new directory: throwaway_example
  • I change into the directory to examine it.
cd throwaway_example
ls
  • Not much there:
Cargo.toml  src

Aside: Tree

  • Can also see directory structure.
  • Install tree with your preferred installer (perhaps apt or brew)
sudo apt install tree
  • Then use tree instead of ls
.
├── Cargo.toml
└── src
    └── main.rs

1 directory, 2 files

main.rs

  • Cargo creates projects with a default main file.
main.rs
fn main() {
    println!("Hello, world!");
}
  • The name “main” is basically mainful to Cargo - it says which, if multiple, .rs files to read first when trying to execute the code in a package.
    • Make sure you understand that sentence.

Cargo Run

Run

  • cargo run is probably our closest equivalent to python3 throwaway_example.py or the “Run” button in VS Code.
  • Of note, you can not simply do “new” then “run”.
    • “New” creates a new folder.
    • “Run” must be done from within that folder.

Try it

  • Do this:
cargo run
  • See this:
   Compiling throwaway_example v0.1.0 (/home/user/throwaway_example)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.56s
     Running `target/debug/throwaway_example`
Hello, world!

Again!

  • If you run again, the process will:
    • Skip the compile step.
    • Be way faster (∞x reported here)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/throwaway_example`
Hello, world!
  • 0.56s -> 0.00s

It’s fast

  • 0.56s -> 0.00s
  • This is why we use languages which compile.
    • Python took around .04s, for me.
    time python3 -c "print('Hello, world')"
  • The results are:
Hello, world

real    0m0.039s
user    0m0.007s
sys     0m0.008s
  • Guess how long it takes to compile python, by the way.

Other files

  • Go ahead and add another .rs file and see what happens.
    • Add it to the src directory!
  • I’ll use this one - I change filename and text, but
secondary.rs
fn secondary() {
    println!("Goodbye, space!");
}

Run it again…

  • Nothing changes.
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/throwaway_example`
Hello, world!
  • This is the same as adding another .py file then not using import or including its code in any other way.

Swap the names.

  • I use mv (move) to change:
    • main.rs to old_main.rs
    • secondary.rs to main.rs
  • Things change!
   Compiling throwaway_example v0.1.0 (/home/user/throwaway_example)
error[E0601]: `main` function not found in crate `throwaway_example`
 --> src/main.rs:3:2
  |
3 | }
  |  ^ consider adding a `main` function to `src/main.rs`

For more information about this error, try `rustc --explain E0601`.
error: could not compile `throwaway_example` (bin "throwaway_example") due to 1 previous error

Requirements

  • You need a main.rs with a function named main.
  • nvim src/main.rs -> Change “secondary” to “main” -> “cargo run”
   Compiling throwaway_example v0.1.0 (/home/user/throwaway_example)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.33s
     Running `target/debug/throwaway_example`
Goodbye, space!

Options

Carry-on only

  • I try to live me life without baggage.
    • (I don’t like cargo)
  • Why? Well…
    • I can’t name things the way I want
    • I can’t add cargo created directories to repositories.
  • Well, I can, but it I have to use options.

Option 1: Names

  • It is uncommon to name a package numerically.
  • It is however, I think, useful in a course.
  • It is also, I think, useful to write packages for a course.
  • So we do so.

Name Scheme

  • I have used the following scheme this term:
    • Each class day is denoted as a two digit value.
    • The first digit represents the zero-indexed week.
    • The second digit represents the zero-index position in ["Lecture", "Lab", "Homework"]

Today

  • Today is one week from the first day of class, last week.
  • Today is one day from the first day of the week, Monday.
  • So I denote today 11

Use Cargo

  • Let’s use cargo to make a package called 11.
cargo new 11
  • What happens?

Problem

    Creating binary (application) `11` package
error: invalid character `1` in package name: `11`, the name cannot start with a digit
If you need a package name to not match the directory name, consider using --name flag.
If you need a binary with the name "11", use a valid package name, and set the binary name to be different from the package. This can be done by setting the binary filename to `src/bin/11.rs` or change the name in Cargo.toml with:

    [[bin]]
    name = "11"
    path = "src/main.rs"

Solution

  • We can make a folder named 11
  • But not a package named 11.
  • We use the --name flag.
    • I use the name of these slides, rather than the number, as the package name.
    cargo new 11 --name packages
  • This should be allowed.

Option 2: VCS

  • It may be the case that you created a 11 folder outside of your 271rs directory.
    • I do this often, then plan to move it in latter.
  • This can pose a problem.

Situation

  • git clone a 271rs repository, e.g.:
git clone git@github.com:cd-c89/271rs.git
- `cargo new 11 --name packages` before `cd 271rs`
- It would look like this:

```{.bash filename="tree"}
.
├── 11
│   ├── Cargo.toml
│   └── src
│       └── main.rs
└── 271rs
    ├── LICENSE
    └── README.md

3 directories, 4 files

Move

  • I’d like to use the mv command to put 11 in 271 rs.
    • Notation is “mv” then “source” then “destination”
    mv 11 271rs
  • I have this:
tree
.
└── 271rs
    ├── 11
    │   ├── Cargo.toml
    │   └── src
    │       └── main.rs
    ├── LICENSE
    └── README.md

3 directories, 4 files

Status

  • Then I cd into 271rs to add/commit/push
git status
On branch main
Your branch is up to date with 'origin/main'.

Untracked files:
  (use "git add <file>..." to include in what will be committed)
        11/

nothing added to commit but untracked files present (use "git add" to track)

Add

  • Add doesn’t work:
git add 11
error: '11/' does not have a commit checked out
fatal: adding files failed
  • By default, cargo initializes new projects as a git repository.
  • Putting a git repository inside another git repository is non-trivial.

Confirm

  • You can tell by looking for the hidden .git folder in 11.
cd 11
ls -al
  • You’ll see something called .git.
  • You can delete it, but this is inelegant.

Solution

  • Use cargo new with the option to have no “version control system” used.
cargo new 11 --name packages --vcs none
  • You can confirm there is no hidden .git folder.
  • This can be moved into 271rs or created within 271rs

Exercise

Exercise

  • New Cargo project, in
  • A folder named 11, with
  • A valid Cargo name (not 11), that is
  • In your course repository and therefore
  • Has no cargo managed repository, and
  • Present on your remote GitHub repository
  • The conents of the project are immaterial.

Disambiguity

  • I have seen students take extreme liberties with naming folders and assignments.
  • I believe no ambiguity exists within the instructions.
  • It is trivial to check the following:
ls 271
  • Unless you are working ahead, a file readout other than the following is wrong, and would be worth no points as it does not constitute submission of assigned work.
01  02  11  LICENSE  README.md

Fin