Welcome on board 🚀

I’m cutting you the straight‑to‑the‑point guide I wish I’d had on day one. In the next couple of hours you’ll:

  1. Bootstrap a secure, repeatable dev machine.
  2. Ship a “hello‑world” Rust CLI locally.
  3. Open your first pull request in Stratorys codebase.

Bookmark this book, keep a terminal open, and treat every code block as something you actually type. If a step fails, open an issue—docs are living code.


Pre‑flight checklist

Item
macOS  / Linux
Admin rights (or someone with sudo)
GitHub account with 2‑factor enabled
Patience for ~3 GB of toolchain downloads

How this book is organised

  • Setup → Security → Git get you ready to clone and build.
  • Rust → Exercises learn and understand the language + practice.
  • Networking → Data Structures other importants knwoledge.
  • Contributing walks you through a tiny feature + PR so you can merge.

Need help?

Ask in #onboarding Slack or ping your mentor. Stuck on a command? Post the exact error and the last step you ran—we debug fast when the signal is high.

Happy hacking ✌️

Operating System Setup

I assume a fresh machine; feel free to skip steps you’ve nailed already.


Common ground (macOS & Linux)

1. Install Homebrew

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

Homebrew manages everything from git to postgresql.

Add Brew to PATH (the installer prints the snippet) and run brew doctor until you’re "ready to brew".

2. Rust toolchain

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

rustup installs stable Rust plus cargo, the build system. Leave default targets.

3. Oh My Zsh (optional but nice)

sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"

Gives you a battery‑included zsh config, plugins, and a pretty prompt.

Enable the git and rust plugins in ~/.zshrc for sweet autocompletions.


macOS notes

  • Install Xcode Command‑Line Tools:
xcode-select --install

Linux (Ubuntu)

sudo apt update && sudo apt install build-essential curl file git
sudo apt install zsh docker.io
chsh -s $(which zsh)
sudo usermod -aG docker $USER

Log out/in to refresh group membership.


Verify setup

rustc --version
cargo new sanity-check && cd sanity-check && cargo run
git --version
docker --version

Editors & IDEs

Pick one primary editor, recommand Zed or VSCode


  1. Install VS Code from the official site. :contentReference[oaicite:0]{index=0}
  2. Launch and hit ⌘⇧P -> Extensions: Install -> search rust‑analyzer.
    rust‑analyzer gives near‑instant completions, inline type hints and “run test”
  3. Add these Rust components:
rustup component add clippy rustfmt

clippy lints common foot‑guns; rustfmt enforces style on save.

  1. Nice‑to‑have extensions:
ExtensionWhy
Error LensHighlights compiler errors inline.

Security

Security is a critical aspect of our development workflow. This section covers essential security practices that every team member must implement.

Best Practices for Security

  • Strong Passwords: Always use strong, unique passwords for all your accounts. A strong password should be at least 16 characters long, combining letters, numbers, and special characters. Avoid using easily guessable information, such as birthdays or common words.
  • Two-Factor Authentication (2FA): Enable two-factor authentication on all accounts that offer it. 2FA adds an extra layer of security by requiring not only a password and username but also something that only the user has on them, i.e., a piece of information only they should know or have immediately to hand - such as a physical token.
  • Lock Your Laptop: Make it a habit to lock your laptop whenever you step away from it, even if it's just for a short period. This practice prevents unauthorized access to company data.
  • Awareness in Public Spaces: When working remotely, especially in public spaces, be mindful of your surroundings. Ensure that no one can overlook your screen. This caution helps prevent shoulder surfing, where an attacker can obtain confidential information by watching your screen.
  • Encrypt Your Hard Drive: Encrypt the hard drive of your laptop and any other devices you use for work. Encryption converts the data stored on your device into unreadable code that cannot be easily deciphered by unauthorized people.
  • SSH Key: Use SSH keys for secure access to your repositories. SSH keys are a pair of cryptographic keys that can be used to authenticate your identity without the need for a password.

These practices are non-negotiable for working with our repositories and systems.

SSH Keys

SSH keys provide secure authentication for Git operations and server access. We standardize on ED25519 keys for their security and performance advantages.

Generating ED25519 SSH Keys

  1. Open Terminal and run:
ssh-keygen -t ed25519 -C "your_email@example.com"
  1. When prompted for a file location, press Enter to use the default location

  2. Enter a secure passphrase (required for our team)

  3. Verify your key was created:

ls -la ~/.ssh

You should see:

  • id_ed25519 (private key - never share this)
  • id_ed25519.pub (public key - safe to share)

Adding Your SSH Key to GitHub

  1. Copy your public key to clipboard:
pbcopy < ~/.ssh/id_ed25519.pub  # macOS
# OR
cat ~/.ssh/id_ed25519.pub  # Linux (copy manually)
  1. Go to GitHub -> Settings -> SSH and GPG keys -> New SSH key

  2. Give your key a descriptive title (e.g., "Work MacBook Pro")

  3. Paste your public key in the "Key" field

  4. Click "Add SSH key"

  5. Test your connection:

ssh -T git@github.com

You should see: "Hi username! You've successfully authenticated..."

Key Management Best Practices

  • Use a password manager to store your SSH key passphrase
  • Never share your private key
  • Use different keys for different services when possible
  • Consider using ssh-agent to avoid typing your passphrase repeatedly:
eval "$(ssh-agent -s)"
ssh-add ~/.ssh/id_ed25519

Git & GitHub

This chapter is the muscle‑memory playbook: minimal theory, maximal commands.
By the end you can clone, branch, commit, rebase, and open a pull‑request that sails through CI.


1. First‑time config

git config --global user.name  "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main
git config --global pull.rebase true    # rebase by default
git config --global color.ui auto

user.name and user.email stamp every commit; GitHub shows them in blame views. The defaults above keep your history linear and readable.


2. Cloning a repo

git clone git@github.com:stratorys/project.git
cd project

git clone creates a working directory, a .git folder, and a remote named origin pointing at GitHub.


3. Branching workflow

git checkout -b feat/login‑oauth # create & switch
git checkout main   # switch to an existing branch

Branches are cheap pointers; create one per task, push early, delete when merged. Upstream rules:

  • prefix feat/fix/chore/hotfix/
  • Keep them rebased on main to avoid diamond graphs.

4. Commit hygiene

Follow Conventional Commits:

feat(auth): add OAuth2 PKCE flow

BODY: rationale, links, screenshots if UI.
BREAKING CHANGE: drops legacy /v1 token endpoint

The spec enables automatic changelogs and semantic version bumps. Aim for focused commits; if your diff is >300 lines, split it.


5. Syncing with main

git checkout main
git pull origin main
git checkout - # `-` is shorthand for previous branch
git rebase main

rebase rewrites your branch atop the latest main so the merge commit stays clean.


6. Remotes & pushing

git remote add origin git@github.com:stratorys/project.git
git push -u origin feat/login‑oauth

-u sets up upstream tracking so future git push / git pull need no extra flags.


7. Opening a pull request

On GitHub: Choose your branch → New pull request → fill template → create.

CLI alternative:

gh pr create --fill
gh pr checkout 123   # review someone else’s PR

gh pr checkout grabs the branch locally for testing. ([GitHub CLI][11])


Quick‑ref commands

git status
git add . # stage all changes
git commit -m "fix: add OAuth2 PKCE flow"
git commit -m "fix: add OAuth2 PKCE flow" --amend # amend last commit
git log --oneline --graph --decorate # view commit history
git diff # view unstaged changes
git diff --cached # view staged changes
git diff HEAD # view all changes since last commit
git reset HEAD~1 # undo last commit (keep changes)
git pull origin main # pull changes from remote
git push origin main # push changes to remote

Databases · PostgreSQL

We run PostgreSQL because it’s ACID‑safe, battle‑tested, and pairs cleanly with Rust ORMs like sqlx and Diesel. Everything here targets Postgres 16 but 95 % works back to 12.


1 · Spin‑up

docker run --rm -d --name postgres -e POSTGRES_USER=postgres -e POSTGRES_PASSWORD=postgres -e POSTGRES_DB=mydb -p 5432:5432 postgres

2 · Connect with psql

psql postgres://postgres:postgres@localhost:5432/postgres

Quick meta‑commands:

CommandWhat it does
\llist databases
\c dbconnect / switch
\dtlist tables
\d tbldescribe table/columns
\qquit

All meta‑commands are baked in psql.


3 · Transactions 101

BEGIN;
UPDATE account SET balance = balance - 10 WHERE id = 1;
UPDATE account SET balance = balance + 10 WHERE id = 2;
COMMIT;   -- or ROLLBACK;

One bad statement ? Roll back and your data stays consistent-as per the SQL standard Postgres implements.


4 · Performance quick wins

  • Indexes-create them on columns used in WHERE or join predicates:
CREATE INDEX idx_user_email ON users (email);

Index syntax and caveats live in the official docs.

  • Explain plans-confirm the planner hits that index:
EXPLAIN ANALYZE SELECT * FROM users WHERE email = 'a@b.com';

Green numbers = faster path, red = full table scan.


5 · UUIDs in Postgres

Postgres ships a native uuid type; we prefer UUIDv7 (time‑ordered) for better index locality in event stores. Use the pgcrypto extension until official uuidv7() ships.


6 · Migrations

We version schema in Git with sqlx migrate (see /contributing/cli_tool.md). One migration = one file pair:

20250511103000_create_users.up.sql
20250511103000_create_users.down.sql

Never edit a committed migration; write a new one.


Next stop → SQL Basics.

SQL Basics

The goal: read and write 80 % of queries you’ll ever need.


1 · SELECT anatomy

SELECT column_list
FROM   table
WHERE  predicate
GROUP  BY cols
HAVING aggregate_predicate
ORDER  BY cols
LIMIT  n OFFSET m;

Only SELECT … FROM … is mandatory; everything else is optional and ordered exactly like above per spec.


1.1 · Filtering (WHERE)

SELECT * FROM orders
WHERE  status = 'shipped'
  AND  created_at >= NOW() - INTERVAL '30 days';

WHERE happens before grouping—filter early for speed.


1.2 · Aggregation (GROUP BY, HAVING)

SELECT country, COUNT(*) AS users
FROM   accounts
GROUP  BY country
HAVING COUNT(*) > 100;

HAVING filters after aggregation; ideal for “top N” style analytics.


2 · Writing Data

INSERT INTO users (id, email) VALUES (gen_random_uuid(), 'a@b.com');

UPDATE users SET last_login = NOW() WHERE id = '…';

DELETE FROM users WHERE last_login < NOW() - INTERVAL '1 year';

Wrap multiple writes in a transaction to keep data consistent.


3 · Data types you’ll meet daily

TypeExampleNotes
INTEGER4232‑bit signed
BIGINT900719925474099164‑bit
TEXT'hello'un‑bounded string
BOOLEANTRUE1‑byte
UUID'550e8400‑e29b‑41d4‑a716‑446655440000'native support, RFC 4122 compliant
JSONB'{"k":"v"}'binary JSON with GIN indexes

Next stop → Joins.

Joins

Tables rarely live alone; joins stitch them. Master these four and you’re set.


1 · INNER JOIN (the default)

SELECT u.id, o.id, o.total
FROM   users AS u
JOIN   orders AS o ON o.user_id = u.id;

Returns rows where the join predicate matches in both tables.


2 · LEFT JOIN (keep left‑hand rows)

SELECT u.id, o.id, o.total
FROM   users u
LEFT JOIN orders o ON o.user_id = u.id;

Users with zero orders still appear; o.* is NULL. Handy for “show latest activity or None”.


3 · RIGHT / FULL OUTER JOIN (rare)

-- right
SELECT * FROM a
RIGHT JOIN b ON b.a_id = a.id;

-- full
SELECT * FROM a
FULL JOIN  b ON b.a_id = a.id;

Use sparingly; often a UNION of two LEFT JOINs is clearer.


4 · WHERE vs ON filtering

-- filter before join
SELECT * FROM a
JOIN b ON b.a_id = a.id AND b.active;

-- filter after join
SELECT * FROM a
JOIN b ON b.a_id = a.id
WHERE b.active;

ON filters rows before they join, reducing work; WHERE filters the joined result. Know the difference or you’ll change row counts.


5 · Good habits

  • Qualify every column (table.col)—future schema edits won’t break queries. Official doc
  • Index join keys on both sides.
  • Check row counts before & after join when debugging.

ASCII refresher

INNER      LEFT         RIGHT         FULL
a ∩ b      a ⊆ a⋃b      b ⊆ a⋃b      a⋃b

Command‑line Basics

You’ll spend more time in a terminal than any GUI, so this chapter is the crash‑course that gets muscle memory on point:

Run every snippet as you read—trying beats memorising.

Core UNIX Commands

The table below is 80 % of what you’ll type each day. Flags shown are the safe defaults we review in PRs.

CommandCheat‑descriptionExample
pwdprints full path of current directorypwd
envdumps environment variables (useful for debugging config)env | grep DATABASE
cdchange directory (cd ‑ jumps back)cd ~/projects
ls ‑lalist files, humans size, dotfiles visible, colorized by defaultls -la ~/Downloads
mkdir ‑pmake directory, parents if neededmkdir -p tmp/cache
cp ‑acopy file/dir, preserve perms (-r not needed with ‑a)cp -a src build/
mvmove or rename file/dirmv foo.txt bar.txt
rm ‑rfdelete recursively, no prompts; triple‑check path first!rm -rf build/
catdump file to stdout (pipe into less -R for paging)cat README.md
grep ‑rsearch recursively for string in filesgrep -r 'foo' src/
ssh -v user@hostsecure shell; -v prints handshake debugssh -v dev@10.0.0.5
sqlx migrate add <migration-name>create a new migration file with the given name.sqlx migrate add init-user
cargo checkcheck the code for errors without building it.cargo check
cargo fmtformat the code according to the Rust style guide.cargo fmt
cargo clippylint the code to find potential issues.cargo clippy
cargo buildbuild the code.cargo build
cargo runrun the code.cargo run
cargo testrun the tests.cargo test
cargo run -p <package>run a specific package.cargo run -p api
cargo add <package>add a new package to the project.cargo add tokio -F full

Further reading

  • “pwd” Linux man page
  • “env” command usage guide on Geeks‑for‑Geeks :contentReference[oaicite:1]{index=1}
  • PhoenixNAP’s cd primer :contentReference[oaicite:2]{index=2}
  • “ls” Linux man page :contentReference[oaicite:3]{index=3}
  • “mkdir(2)” Linux man page :contentReference[oaicite:4]{index=4}
  • Wikipedia’s overview of cp :contentReference[oaicite:5]{index=5}
  • LinuxCommand.org lesson on moving files :contentReference[oaicite:6]{index=6}
  • Tutorialspoint rm command guide :contentReference[oaicite:7]{index=7}
  • cat(1) Linux man page :contentReference[oaicite:8]{index=8}
  • freeCodeCamp tutorial on grep :contentReference[oaicite:9]{index=9}

Shell & Prompt Tooling

zsh is the default on modern macOS and many Linux distros. Stick to it; completions and plugins are better than old‑school bash.


1 - Install Oh My Zsh

Run the official one‑liner installer; it clones a git repo into ~/.oh‑my‑zsh and appends bootstrap code to ~/.zshrc. Enable plugins we lint in CI:

plugins=(git rust docker)

Reload with source ~/.zshrc. Oh My Zsh auto‑updates weekly unless you disable it.


2 - Homebrew

Homebrew is our cross‑platform package manager. The install script detects Apple Silicon vs Intel and places binaries under /opt/homebrew or /usr/local accordingly. Post‑install, brew doctor checks your PATH.

brew install bat
brew upgrade && brew autoremove

3 - Add an alias for cat

bat is a drop‑in replacement for cat with syntax highlighting and line numbers. It’s a great way to view files in the terminal.

nano ~/.zshrc
# Add this line to your ~/.zshrc file
# to use bat as a replacement for cat
# This will show line numbers and colorize the output
# when you use the cat command
alias cat='bat --style=numbers --color=always'

3.3 - Tips

cat -p # Use -p to disable formatting and show raw output

4 - Make zsh your default (macOS)

chsh -s $(which zsh)

Rust Basics

Rust is a compiled, statically typed language that focuses on safety and zero‑cost abstractions. By the end of this primer you’ll recognise the core syntax, read production code, and write simple functions—ready for deeper topics like ownership, lifetimes, and async.


Variables & Mutability

fn main() {
    let x = 5;          // immutable by default
    let mut y = 5;      // mutable
    y += 1;
    println!("{x} {y}");
}

Values are immutable unless you add mut. Shadowing (let x = x + 1;) lets you reuse a name while leaving the original binding unchanged.

Rust’s variable bindings


Data Types

#![allow(unused)]
fn main() {
let a: i32  = 42;            // 32‑bit signed
let b: f64  = 3.14;          // 64‑bit float
let c: char = '🦀';          // Unicode scalar
let d: bool = true;

let tup: (i32, f64, bool) = (a, b, d);
let (x, y, z) = tup;         // destructuring
let arr = [1, 2, 3];         // fixed‑size array (stack)
let mut vec = vec![1, 2, 3]; // growable vector (heap)
}

Rust’s primitive types


Functions

fn add(a: i32, b: i32) -> i32 {
    a + b        // implicit return (no semicolon)
}

fn main() {
    println!("{}", add(2, 3));
}

A function starts with fn; parameters are type‑annotated. The last expression, without a semicolon, becomes the return value.

Rust’s functions


Comments

#![allow(unused)]
fn main() {
// single‑line
/* block */
//! crate‑level documentation
/// item‑level docs collected by `rustdoc`
}

Line comments explain why, not what. Doc comments (/// and //!) generate HTML docs via cargo doc.

Rust’s comments


Control Flow

#![allow(unused)]
fn main() {
for n in 1..=5 {
    if n % 2 == 0 { println!("{n} even"); }
}

let mut i = 0;
while i < 3 {
    i += 1;
}
}

Rust offers if/else, while, and for; any loop can break or continue.

Rust’s control flow


Structs

struct User {
    id: u32,
    email: String,
    active: bool,
}

fn main() {
    let mut user = User { id: 1, email: "a@b.com".into(), active: true };
    user.active = false;
}

Structs bundle related fields. The whole instance must be mutable to change any field.

Rust’s structs Rust’s struct design patterns

Methods

#![allow(unused)]
fn main() {
impl User {
    fn deactivate(&mut self) {
        self.active = false;
    }
}
}

Methods live in impl blocks and take self, &self, or &mut self as the first parameter.

Rust’s struct design patterns


Enums & Pattern Matching

#![allow(unused)]
fn main() {
enum Direction { North, East, South, West }

fn compass(dir: Direction) {
    match dir {
        Direction::North => println!("Up"),
        Direction::South => println!("Down"),
        _ => println!("Sideways"),
    }
}
}

match is exhaustive, forcing you to handle every variant. Use if let or let else for concise single‑pattern checks.

Rust’s enums Rust’s match Rust’s if let


Packages, Crates & Modules

cargo new hello-cli       # binary crate
cargo new util --lib      # library crate

A package is one or more crates plus Cargo.toml. Use mod to declare modules and use to bring items into scope; split large modules into multiple files for clarity.

#![allow(unused)]
fn main() {
mod db {
    pub fn connect() {}
}

use crate::db::connect;
}

Rust’s modules


Error Handling

#![allow(unused)]
fn main() {
fn read() -> std::io::Result<String> { /* ... */ }

if read().is_err() {
    eprintln!("failed");
}

match read() {
    Ok(text) => println!("{text}"),
    Err(e)   => eprintln!("error: {e}"),
}
}

Use is_err() for quick checks; prefer match (or the ? operator) when you need the Ok value or fine‑grained error logic. Reserve panic! for unrecoverable bugs.

Rust’s error handling Rust’s Result type Rust’s ? operator


Generics, Traits & Lifetimes — a preview

#![allow(unused)]
fn main() {
fn largest<T: PartialOrd>(slice: &[T]) -> &T {
    let mut max = &slice[0];
    for item in slice {
        if item > max { max = item; }
    }
    max
}
}

Generics enable type‑agnostic code, traits define shared behaviour, and lifetimes tell the compiler how references relate. You’ll dive deeper in later chapters.

Rust’s generics Rust’s traits Rust’s lifetimes

Ownership & Borrowing

1 - The Three Rules of Ownership

  1. Every value has one owner.
  2. When the owner goes out of scope, the value is dropped.
  3. A value can be borrowed by reference, but while it’s mutably borrowed no other borrows are allowed.

These rules are enforced at compile‑time, so there’s zero runtime overhead.


2 - Move Semantics

fn main() {
    let s1 = String::from("hello"); // s1 owns heap data
    let s2 = s1;                     // ownership moved; s1 invalid
    // println!("{s1}");            // 🔴 compile‑error: value borrowed after move
    println!("{s2}");               // ✅ ok

    let n1 = 5;          // i32 implements Copy
    let n2 = n1;         // bits duplicated; both variables usable
    println!("{n1} {n2}");
}

Types with the Copy trait (numbers, bools, chars, arrays of Copy) are duplicated; everything else moves by default.


3 - Immutable Borrowing

fn main() {
    let data = String::from("rust");
    let len = length(&data); // borrow read‑only
    println!("{data} has length {len}");
}

fn length(s: &String) -> usize { s.len() }

Any number of &T (immutable) references may coexist—perfect for read‑heavy scenarios.


4 - Mutable Borrowing

fn main() {
    let mut buf = String::from("hi");
    shout(&mut buf);     // exclusive access
    println!("{}", buf); // "HI"
}

fn shout(s: &mut String) { s.make_ascii_uppercase(); }

A &mut T gives one writer exclusive control. No other borrows (mutable or immutable) may exist in the same scope.


4.1 - Why Mixed Borrows Fail

#![allow(unused)]
fn main() {
let mut v = vec![1, 2, 3];
let item = &v[0];   // immutable borrow
v.push(4);          // 🔴 cannot borrow `v` as mutable because it’s also borrowed as immutable
println!("{item}");
}

Rust prevents reads that could point to reallocated memory after push().

Fixes: shorten the immutable borrow’s scope, clone the data, or restructure code.


5 - Lifetimes in 60 Seconds

A lifetime is the span during which a reference is valid. The compiler tags each reference with a lifetime parameter ('a, 'b, …) and makes sure they don’t outlive the data they point to.

#![allow(unused)]
fn main() {
fn longest<'a>(x: &'a str, y: &'a str) -> &'a str {
    if x.len() > y.len() { x } else { y }
}
}

Usually lifetimes are inferred; you specify them when multiple borrows overlap and Rust can’t guess.


6 - Slices: Borrowing Parts of Collections

#![allow(unused)]
fn main() {
fn first_word(s: &str) -> &str {
    let bytes = s.as_bytes();
    for i in 0..bytes.len() {
        if bytes[i] == b' ' { return &s[..i]; }
    }
    s
}

let sentence = String::from("hello world");
let word = first_word(&sentence);
println!("{word}"); // "hello"
}

A slice (&[T] or &str) borrows a contiguous range without copying.


7 - Interior Mutability & Shared Ownership

When you need multiple owners and mutation, reach for these tools:

ToolWhat it does
Rc<T>Single‑threaded reference counting
Arc<T>Thread‑safe reference counting
RefCell<T>Run‑time borrow checking (single thread)
Mutex<T> / RwLock<T>Mutual exclusion for data inside Arc

Example: mutate across threads with Arc<Mutex<T>>.

#![allow(unused)]
fn main() {
use std::sync::{Arc, Mutex};
use std::thread;

let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];

for _ in 0..10 {
    let c = Arc::clone(&counter);
    handles.push(thread::spawn(move || {
        let mut num = c.lock().unwrap();
        *num += 1;
    }));
}

for h in handles { h.join().unwrap(); }
println!("counter = {}", *counter.lock().unwrap());
}

The borrow checker trusts the Mutex at runtime to enforce exclusive access.


8 - Common Compile‑Time Errors & Fixes

Error message snippetWhy it happensTypical fix
value borrowed here after moveyou used a value after ownership transferredclone or restructure code
cannot borrow as mutable because it is also borrowed as immutablea &mut overlaps an &shorten scope, split function, or use interior mutability
missing lifetime specifiercompiler can’t infer lifetimesadd explicit lifetime parameters

9 - Hands‑On Exercises

  1. Fix the mixed‑borrow error
fn main() {
    let mut data = vec![1, 2, 3];
    let first = &data[0];
    data.push(4);
    println!("{first}");
}
  1. Return the biggest number without cloning
#![allow(unused)]
fn main() {
fn largest(slice: &[i32]) -> &i32 {
    // TODO
}
}

Async