Packages

Systems in Rust

Author

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

Footnotes

  1. Well, kinda. We’ll get to that.↩︎