# Cargo – konvention framför konfiguration

4 september 2026 | https://www.ribomation.se/blog/2026/cargo-konvention-framfor-konfiguration/

Hur många gånger har du inte öppnat ett nytt C++ projekt och först behövt lista ut om det byggs med `make`, `CMake`, `Bazel` eller ett handskrivet skript — och sen vilket av `Conan`, `vcpkg` eller `FetchContent` som sköter tredjepartsbiblioteken? I Rust-världen finns inte den frågan. Ett enda verktyg, `cargo`, skapar projektet, kompilerar det, kör testerna, genererar dokumentationen och hämtar hem beroenden — med samma kommandon, i alla projekt, från dag ett. I den här artikeln går jag igenom hur cargo fått till detta, varifrån idén kommer, och avslutar med ett litet demo-program som lånar in ett par tredjepartsbibliotek för att rita en Normal-fördelning som ett stjärn-histogram.

* * *

Kompilering av Rust
-------------------

Med bara en enda källkodsfil `hello.rs` är det enkelt att kompilera till en exekverbar fil.

```rust
fn main() {
    println!("Hello from a Rust program");
}
```

```shell
$ rustc hello.rs -o hello-rs
$ ./hello-rs
Hello from a Rust program
```

Principen är ju den samma som för en C++ källkodsfil `hello.cxx`.

```cpp
#include <print>
int main() {
    std::println("Hello from a C++ program");
}
```

```shell
$ g++ -std=c++23 -Wall hello.cxx -o hello-cxx
$ ./hello-cxx
Hello from a C++ program
```

### Blir det `*.o` filer också i Rust?

Javisst, det fungerar på samma sätt. Normalt genererar kompilatorn dessa i en temporär-katalog och tar bort dessa vid ett lyckosamt resultat. I Rust kan man be kompilatorn om att spara `*.o` filen på följande vis.

```shell
$ rustc --emit=obj,link hello.rs -o hello-rs
$ ls -lhF *-rs*
-rwxrwxr-x 1 jensr jensr 4.2M Sep  3 16:37 hello-rs*
-rw-rw-r-- 1 jensr jensr 5.6K Sep  3 16:37 hello-rs.o
$ nm --demangle hello-rs.o
0000000000000000 r GCC_except_table8
0000000000000000 T main
0000000000000000 T std::rt::lang_start::<()>
0000000000000000 t std::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>
0000000000000000 t std::rt::lang_start::<()>::{closure#0}
0000000000000000 T hello::main
                 U std::rt::lang_start_internal
                 U std::io::stdio::_print
# Stripped for brievity
```

För C++ ser ut det ut på liknande sätt, fast man behöver utföra det i två steg. Först kompileringen och sedan länkningen.

```shell
$ g++ -std=c++23 -Wall -c hello.cxx -o hello-cxx.o
$ g++ hello-cxx.o -o hello-cxx
$ ls -lhF *-cxx*
-rwxrwxr-x 1 jensr jensr 241K Sep  3 16:44 hello-cxx*
-rw-rw-r-- 1 jensr jensr 458K Sep  3 16:42 hello-cxx.o
$ nm --demangle hello-cxx.o
...812 lines of output...
```

* * *

Byggnationsverktyg
------------------

Så fort man har fler än några få källkodsfiler blir det snabbt besvärligt att hålla reda på olika kompileringskommandon. I UNIX barndom (första hälften av 1970-talet) var _shell-scripts_ ett viktigt hjälpmedel. Ett dylikt är en textfil med vilka kommandon som ska utföras och operativet (kernel) känner igen ett skript och utför instruktionerna.

Med ett flertal källkodsfiler och olika typer av genereringar med flera, blev det besvärligt även med shell-scripts, eftersom man gärna önskade att bara kompilera om de filer som behövdes.

Stuart Feldman vid Bell Labs (det legendariska stället som frambringat UNIX, C, C++ och mycket annat) skrev den första versionen av `make`, där man kunde skapa kompileringsregler och verktyget beräknade vilka filer som behövde omkompileras och i vilken ordningsföljd. Under flera decennier var `make` det primära byggverktyget, även för Java under andra hälften av 1990-talet.

För just Java-världen —i takt med att språket blev allt populärare— blev det pinsamt att man inte kunde _bygga_ plattformsoberoende, eftersom det var ju själva språkets USP (_Unique Selling Point_). Direkt efter millennieskiftet skapade James Duncan Davidson Java-verktyget `Ant` (_Another Neat Tool_), där man skapade byggregler i en XML-fil.

I takt med att Java blev ännu mer populärt och det skapades en kopiös mängd av open-source bibliotek, uppstod behovet av en rationell och enkel hantering av primära bibliotek och alla deras sekundära bibliotek (beroenden). Några år senare (2002) skapade Jason van Zyl den första versionen av `Maven`, vilket snabbt kom att ersätta Ant. Maven byggde på några genomgripande principer:

*   **Configuration by Conventions**
    *   En standardstruktur för projektkatalogen, så hittar alla verktyg källkodsfilerna
*   **Standard set of commands**
    *   Standardiserade kommandon för att kompilera, testa, bygga, skapa dokumentation
*   **Dependency Management & Maven-Central**
    *   Via en s.k. maven-koordinat (grupp, namn, version) kunde verktyget självt ladda ned bibliotek och inkorporera dessa för byggnation och exekvering
*   **Plugins**
    *   Maven core är en plugin-motor med de väsentliga reglerna inbyggda, resten utgörs av plugins, vilka kan kompletteras eller ersättas och möjliggör obegränsat med utökningar

Det här med att skapa en central plats för bibliotek (server) och ett kommandoradsprogram, som söker, laddar ned, installerar och inkorporerar bibliotek, tog Jason från CPAN.

Under 1990-talet i takt med att språket Perl blev populärt uppstod behovet av en dylik tjänst. CPAN (_Comprehensive Perl Archive Network_) skapades i början på 1990-talet och bidrog starkt till att göra Perl industriellt tillämpbart. Man brukade skämta om att nästa krasch på Wall Street kunde bero på en bugg i ett Perl program.

CPAN var emellertid inte först med detta utan inom TeX-världen (typsättning för matematisk text) skapades några år innan, CTAN (_Comprehensive TeX Archive Network_). CPAN har fått många efterföljare inklusive CEAN (Comprehensive Erlang Archive Network) som Joe Armstrong vid Ericsson/Ellemtell i Älvsjö satte upp i slutet på 1990-talet.

Sedan CPAN och Maven Central, så har alla moderna programmeringsspråk en motsvarighet, dels en gemensam server med sökbarhet och en massa bibliotek och dels ett CLI program som sköter hanteringen på utvecklarsidan.

Emellertid, är det synd och skam att vi inte har motsvarande inom C++ världen. Det finns dessvärre flera konkurrerande system, såsom **Conan**, **vcpkg** med flera. Även `CMake` har sin egen `FetchContent`. Detta sammantaget gör att ingen hittills kunnat fungera enande, utan vi har en diversifierad soppa av olika byggverktyg.

* * *

Cargo
-----

Rust är tack och lov ett modernt programmeringsspråk och har därför den typ av supportsystem man numera förväntar sig. Byggverktyget för Rust heter `cargo` och täcker i stort sett hela arbetsflödet runt ett Rust-projekt:

*   **projektskapare** — `cargo new` sätter upp mappstruktur, manifest och ett git-repo
*   **pakethanterare** — `cargo add` hämtar tredjepartsbibliotek från registret [crates.io](https://crates.io) och håller reda på exakta versioner
*   **byggverktyg** — `cargo build` kompilerar projektet och allt det beror på, i rätt ordning, parallellt
*   **testkörare** — `cargo test` hittar och kör alla tester i projektet
*   **dokumentationsgenerator** — `cargo doc` bygger en HTML-sajt ur doc-kommentarerna
*   **publiceringsverktyg** — `cargo publish` laddar upp en crate till crates.io (utanför den här artikelns ämne)

Poängen är att allt detta följer med. Installerar man Rust via `rustup` får man `rustc` (kompilatorn) och `cargo` i samma svep, och `cargo` är i praktiken det enda kommando man behöver lära sig.

Så här skapar men ett nytt Rust-projekt:

```shell
$ cargo new hello
    Creating binary (application) `hello` package
```

Resultatet är minimalt:

```text
hello
├── Cargo.toml        # projektets manifest
├── .gitignore        # innehåller raden /target
└── src
    └── main.rs       # fn main() { println!("Hello, world!"); }
```

Cargo initierar dessutom ett git-repo på köpet. Med option `--vcs <name>` kan man initiera för andra versionshanteringssystem (VCS - Version Control System), där name kan vara ett av `git`, `hg`, `pijul`, `fossil`, `none`. Den sistnämnda innebär inget av dem.

Designen av cargo har inspirerats av Jason van Zyl's princip om _configuration by conventions_. All källkod ligger under katalogen `src/` och filnamnet `main.rs` är fixerat och styr att cargo ska bygga en exekverbar fil. Direktiven till cargo är standardiserade likväl som i Maven, `cargo build|test|run|...`. Alla byggartefakter hamnar i katalogen `target/`, precis som i Maven.

### Konfiguration (_Cargo.toml_)

Cargo har en primär konfigurationsfil (plus en för avancerade byggen) kallad `Cargo.toml` och är på formatet TOML (_Tom's Obvious, Minimal Language_). Detta format skapades av Tom Preston-Werner och är en renodlad och standardiserad form av INI-filer från den gamla MS-DOS tiden. Så här kan en `Cargo.toml` se ut:

```toml
[package]
name = "hello"
version = "0.1.0"
edition = "2024"

[dependencies]
```

Här ser vi namn och version på projektet, samt vilken språk-version projektet använder sig av. `edition` är Rusts sätt att införa språkändringar utan att bryta gammal kod. Giltiga värden är `2015`, `2018`, `2021` eller `2024`. Edition-värdet har viss likhet med `-std=c++23` i C++, men är mer genomgripande.

### Lådprojekt (_crate_)

Ett Cargo-projekt benämnes som `crate`. Ordet betyder låda på svenska (faktiskt även ölback, men det tiger vi om). Vi ser också att det finns ett tomt avsnitt för externa bibliotek (`dependencies`), dvs _external dependency crates_. Detta ska vi diskutera litet senare i denna artikel.

* * *

Programbyggnation
-----------------

Man kan kompilera och länka separat först (`cargo build`) och sedan exekvera (`cargo run`), eller låta den sistnämnda utföra det förstnämnda.

```shell
$ cargo run
   Compiling hello v0.1.0 (/home/jensr/hello)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.12s
     Running `target/debug/hello`
Hello from a Rust program
```

* * *

Bibliotek
---------

En crate kan också definiera ett bibliotek, dvs en crate som länkas till andra crates och bildar ett program (exekverbar fil). Genom att byta namn på `src/main.rs` till `src/lib.rs`, så hux flux har man ett biblioteks-projekt (library crate). Som synes, styr filnamnet huruvida ens crate utgör en _executable_ eller _library_.

```text
my_project
├── Cargo.toml
└── src/
    └── lib.rs
```

Måste man välja? Nej, en crate kan ha båda formerna `src/{main,lib}.rs`. Själva filnamnet är dock fixerat. Givetvis kan man dela upp källkoden på flera filer, så kallade moduler, vilket vi ska diskutera härnäst.

* * *

Moduler
-------

I ett C++ program delar man upp koden i flera filer (_.cxx), kompilerar dessa separat till objekt-filer (_.o) och skriver inkluderings-filer (\*.hxx) innehållande typ-definitioner och funktions-deklarationer, så att kompilatorn och länkaren kan para ihop ett anrop av en funktion med var denna är definierad. Med C++20 kom modules, som är tänkt att förenkla denna hantering och korta byggtiderna. Emellertid, är detta, ett drygt halvt decennium senare, fortfarande på experimentstadiet.

Bakom kulisserna i Rust försiggår ungefär samma operationer, men språket har haft stöd för moduler redan från första början. Även här följer man _configuration by conventions_, genom att skapa en underkatalog med modul-namnet och en fil med det standardiserade namnet `mod.rs`.

```text
my_project
├── Cargo.toml
└── src/
    ├── main.rs
    └── numbers/
        └── mod.rs
```

Sedan, måste källkodsfilen på samma katalognivå deklarera modulen via `mod <name>`.

```rust
mod numbers;
fn main() { . . . }
```

Det finns en principiell och en pragmatisk sida av detta. Beträffande det sistnämnda så klagade utvecklare på att ha en bunt med filer med samma namn (`mod.rs`) öppna i sin utvecklingsmiljö (IDE). Så från edition 2018 och `rustc 1.30`, infördes en förenkling; man skapar kort och gott en Rust fil med modul-namnet, på samma nivå som den som definierar modulen. Så här kan det se ut då:

```text
my_project
├── Cargo.toml
└── src/
    ├── main.rs
    └── numbers.rs
```

För mindre moduler, räknat i antal rader kod, kan man placera koden direkt i den fil som deklarerar denna. Detta kallas för en _inline module_.

```rust
fn main() {
    println!("origo: {:?}", geometry::origin());
}

mod geometry {                                                                              
    #[derive(Debug)]
    pub struct Point { pub x: f64, pub y: f64 }                                             
    pub fn origin() -> Point { Point { x: 0.0, y: 0.0 } }                                   
} 
```

Rust använder sig frekvent av annoteringar med syntaxen `#[action(key=value...)]`. En vanlig annotering är `cfg(...)` som står för _configuration_. Ett sådant exempel är plattforms-beroende definitioner:

```rust
#[cfg(target_os = "linux")]
mod platform {  
  pub fn config_dir() -> String {  
      std::env::var("XDG_CONFIG_HOME")  
          .unwrap_or_else(|_| format!("{}/.config", std::env::var("HOME").unwrap()))  
  }  
  pub const LINE_ENDING: &str = "\n";  
}  

#[cfg(target_os = "windows")]  
mod platform {  
  pub fn config_dir() -> String {  
      std::env::var("APPDATA").unwrap()  
  }  
  pub const LINE_ENDING: &str = "\r\n";  
}  

#[cfg(target_os = "macos")]  
mod platform {  
  pub fn config_dir() -> String {  
      format!("{}/Library/Application Support", std::env::var("HOME").unwrap())  
  }  
  pub const LINE_ENDING: &str = "\n";  
}  

fn main() {  
  println!("config: {}", platform::config_dir());  
  print!("done{}", platform::LINE_ENDING);  
}
```

Här ovan har vi också villkorlig kompilering (_hejsan_ `#ifdef`), bara en av dessa tre alternativ stannar kvar, resten plockas bort. Givet då att man inte försöker kompilera på en fjärde plattform... 🫣

Annoteringarna ovan är inte på något sätt begränsade till inline modules, utan kan appliceras på vanliga fil-baserade moduler också.

* * *

Multipla program
----------------

För en crate gäller att man kan definiera högst ett bibliotek, men noll, ett eller flera program. Vi har ju sett att ett program definieras med `src/main.rs`, så hur skapar man övriga? Jo, genom att placera dessa under `src/bin/`. Om man så vill kan man betrakta `main.rs` som det primära programmet och övriga i `src/bin/` som sekundära. Emellertid, kanske man inte har något program som är primärt, eftersom alla är likvärdiga. Då är det bättre att lägga samtliga program under `src/bin/` för symmetrins skull.

```text
my_project
├── Cargo.toml
└── src/
    ├── lib.rs
    ├── numbers.rs
    └── bin/
        ├── generate.rs
        └── print.rs
```

Hur anropar man då ett av flera definierade program? Jo, genom att bifoga namnet.

```shell
$ cargo run --bin generate
$ cargo run --bin print
```

Nu kan det vara så att man önskar att ett av dessa program ska kunna anropas utan att ange dess namn, vilket låter sig göras i konfigurationsfilen med `default-run`.

```toml
# Cargo.toml
[package]
name = "hello"
default-run = "print"
. . .
```

Vidare, kanske man anser att namnet på den genererade exekverbara filen ska vara något annat, vilket också kan åtgärdas i konfigurationsfilen med ett (eller flera) `[[bin]]` block. Dubbla hakparenteser i TOML betyder att man skapar en lista, dvs multipla block.

```toml
# Cargo.toml
[package]
name = "hello"

[[bin]]
name = "print-baby-print"
path = "src/bin/print.rs"
# if you read my article about Apollo 11, you get the joke
. . .
```

Om man har både ett bibliotek plus ett (eller flera) program, så måste detta (dessa) importera biblioteket. Namnet fås av vad som anges i `Cargo.toml`. Givet att projektet ser ut som nedan:

```text
my_project
├── Cargo.toml
└── src/
    ├── lib.rs
    └── main.rs
```

```toml
# Cargo.toml
[package]
name = "hello"
. . .
```

Så, ser Rust koden ut som följer:

```rust
// src/lib.rs
pub fn message() {
    let txt = "Tjabba Habba";
    println!("{} --> {}", txt, txt.to_uppercase());
}
```

```rust
// src/main.rs
use hello::message;
fn main() {
    println!("Hello from a Rust program");
    message();
}
```

Kör man sen programmet, ser det ut så här:

```shell
$ cargo run
   Compiling hello v0.1.0 (/home/jensr/RustroverProjects/my_project)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.11s
     Running `target/debug/hello`
Hello from a Rust program
Tjabba Habba --> TJABBA HABBA
$
$ ./target/debug/hello 
Hello from a Rust program
Tjabba Habba --> TJABBA HABBA
```

* * *

Testprogram
-----------

Det finns två testformer i Rust (det finns en tredje form, som jag vägrar kalla "test" - återkommer); enhetstest (_unit tests_) och integrationstest (_integration tests_).

Den första formen definieras _vanligtvis_ med en inline module med namnet `tests` och annoterad med `#[cfg(test)]`, samt varje testfunktion annoteras med `#[test]`.

```rust
pub fn sum(n: i32) -> u64 {
    (n * (n + 1) / 2) as u64
}

#[cfg(test)]
mod tests {
    use super::*; //import parent
    // use crate::numbers::sum; //fully qualified import

    #[test]
    fn sum_of_zero_should_return_zero() {
        assert_eq!(sum(0), 0);
    }
    #[test]
    fn sum_of_one_should_return_one() {
        assert_eq!(sum(1), 1);
    }
    #[test]
    fn sum_of_ten_should_return_fiftyfive() {
        assert_eq!(sum(10), 55);
    }
}
```

Sen kör man alla test med `cargo test`.

```shell
$ cargo test
...
running 3 tests
test numbers::tests::sum_of_one_should_return_one ... ok
test numbers::tests::sum_of_ten_should_return_fiftyfive ... ok
test numbers::tests::sum_of_zero_should_return_zero ... ok

test result: ok. 3 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.00s
...
```

Den andra formen utgörs av separata (test-)program i katalogen `tests/` och körs tillsammans med övriga test. Varje testfunktion ska annoteras på samma sätt som för enhetstesterna.

```text
my_project
├── Cargo.toml
├── src/
│   └── lib.rs
└── tests/
    └── numbers_test.rs
```

* * *

Genererad programdokumentation
------------------------------

Ända sedan James Gosling införde genererad programdokumentation (JavaDoc), baserad på en idé av Donald Knuth (han med TeX och algoritm-böckerna) kallat _literate programming_, så har varje självrespekterande programmeringsspråk något liknande. Självfallet har Rust sin variant (`rustdoc`), inspirerad av javadoc-syntaxen och viss mån doxygen i C/C++-världen.

```text
//! Simple _number-oriented_ functions
//!
//! # Examples
//! ```
//! use hello::numbers;
//! let k = 10;
//! println!("SUM(1..{}) = {}", k, numbers::sum(k));
//! assert_eq!(numbers::sum(10), 55);
//! ```

/// Computes the sum `1+2+...+n`
/// using the Gauss summation formula.
pub fn sum(n: u32) -> u64 {
    (n * (n + 1) / 2) as u64
}
```

Syntaxen i en doc-comment är Markdown, som du kan se i kod-fragmentet. Kursiv text `_number-oriented_` respektive rubrik `# Examples`.

Du har säkert redan noterat att det finns två kommentarsformer; `/// ...` och `//! ...`. Skillnaden är lite subtil och lätt att förbise; den första används _före_ ett element, såsom funktionen `sum()` ovan. Den andra används _inuti_ ett element, i detta fall inuti modulen `numbers`.

Alternativt, för den som föredrar den javadoc inspirerade syntaxen ser det ut så här:

```text
/*! Simple _number-oriented_ functions
 *
 * # Examples
 *
 * ```
 * use hello::numbers::sum;
 * let k = 10;
 * println!("SUM(1..{}) = {}", k, sum(k));
 * assert_eq!(sum(10), 55);
 * ```
 */ 

/** 
 * Computes the sum `1+2+...+n` using the Gauss summation formula.
 */
pub fn sum(n: u32) -> u64 {
    (n * (n + 1) / 2) as u64
}
```

Du genererar dokumentationen med `cargo doc` (föga förvånande). Vad som inte är uppenbart till att börja med är att bara publika medlemmar av definierat bibliotekt tas med. Av det skälet är det vanligt att man under utvecklingen tar med både privata medlemmar och alla program, på följande vis:

```shell
$ cargo doc --document-private-items --bins --open
 Documenting hello v0.1.0 (/home/jensr/RustroverProjects/simple-project)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.68s
     Opening /home/jensr/RustroverProjects/simple-project/target/doc/hello_app/index.html
```

### Kodfragment i dokumentationen

Om du kikar noga i dokumentationskommentaren ovan, ser du att det finns kodfragment enligt Markdown-syntaxen. Dylika fragment kompileras i samband med genereringen, så att man snabbt kan bli varse eventuella regressionsproblem (någon ändrar i produktionskoden men glömmer att uppdatera kommentaren).

Om fragmentet också innehåller testsatser såsom `assert_eq!(sum(10), 55);`, utförs dessa som en "tredje" form av test, kallat _doctests_, i samband med att man kör `cargo test`.

* * *

Externa tredjeparts-bibliotek
=============================

Som jag var inne på i början av denna artikel, så utgör möjligheten att inkorporera främmande bibliotek en essentiell komponent i ett byggnationsverktyg. Cargo har givetvis denna funktionalitet precis som Maven i Java-världen.

Första steget är att leta reda på vilket eller vilka bibliotek man behöver. I Java-världen söker man hos [MVN Repository](https://mvnrepository.com/repos/central), i Node.js-världen söker man hos [NPM.js](https://www.npmjs.com/) och så vidare.

I Rust-världen söker man på [crates.io](https://crates.io) (det officiella registret) eller alternativet [lib.rs](https://lib.rs) (ett tredjepartsgränssnitt med bättre kategorisering och kvalitetssignaler). Dokumentationen för _varje_ publicerad crate byggs automatiskt och ligger på [docs.rs](https://docs.rs) — ingen behöver hantera en server för sina egna API-docs.

Att lägga till ett beroende görs med `cargo add`, som använder den senaste versionen och uppdaterar konfigurationsfilen:

```shell
$ cargo add rand
    Updating crates.io index
      Adding rand v0.10.2 to dependencies
             Features:
             + std
             + std_rng
             + thread_rng
             - serde
             - simd_support
             ...
```

```toml
# Cargo.toml
[dependencies]
rand = "0.10.2"
```

Ett versionskrav som `"0.9"` är inte "exakt 0.9" utan ett _intervall_. Cargo följer [SemVer](https://semver.org) (_major.minor.patch_): en inkrementering av den major betyder "kan bryta bakåtkompatibilitet".

`cargo build` skapar också **`Cargo.lock`**, som listar den _exakta_ version som valdes för varje crate i hela grafen — direkta beroenden såväl som deras beroenden. Nästa bygge, på vilken maskin som helst, använder exakt samma versioner tills man kör `cargo update`. Det här är precis samma typ av hantering som `package-lock.json` i Node.js-världen. Man kan lista alla beroenden av ett projekt med `cargo tree`.

```shell
$ cargo tree --depth 2
hello v0.1.0 (/home/jensr/RustroverProjects/simple-project)
├── rand v0.10.2
│   ├── chacha20 v0.10.2
│   ├── getrandom v0.4.3
│   └── rand_core v0.10.1
└── rand_distr v0.6.0
    ├── num-traits v0.2.19
    │   [build-dependencies]
    └── rand v0.10.2 (*)
```

### Plottning av Normal-fördelning

Som avslutning på denna artikel visar jag ett litet program som använder två externa bibliotek och plottar en typisk Normal-fördelning som ett histogram av stjärnor. Först behöver vi lägga till våra två beroenden:

```shell
$ cargo add rand_distr rand
```

Vilket uppdaterar konfigurationsfilen på följande vis:

```toml
# Cargo.toml
# ...
[dependencies]
rand = "0.10.2"
rand_distr = "0.6.0"
```

Själva programmet ser ut så här:

```rust
use rand::rng;
use rand_distr::{Distribution, Normal};

const N: usize       = 10_000;       // number of samples
const MEAN: f64      = 0.0;          // parameters
const STDDEV: f64    = 2.0;
const BUCKETS: usize = 21;           // histogram resolution
const BOUND: f64     = 4.0 * STDDEV; // histogram range
const LOW: f64       = -BOUND;
const HIGH: f64      = BOUND;
const MAX_STARS: f64 = 40.0;         // max width of stars

fn main() {
    // 1. Generate the samples into a Vec.
    let mut rng = rng();
    let samples: Vec<f64> = Normal::new(MEAN, STDDEV).unwrap()
        .sample_iter(&mut rng)
        .take(N)
        .collect();

    // 2. Aggregate into a histogram table (bucket index -> count).
    let width = (HIGH - LOW) / BUCKETS as f64;
    let mut counts = vec![0usize; BUCKETS]; //initialize with BUCKETS number of zeroes
    for &x in &samples {
        let raw = ((x - LOW) / width).floor() as isize; //isize similar to long int
        let idx = raw.clamp(0, BUCKETS as isize - 1) as usize; //usize similar to unsigned long int
        counts[idx] += 1;
    }

    // 3. Print it: one row per bucket, N stars proportional to the count.
    let peak = counts.iter().copied().max().unwrap_or(1).max(1) as f64;
    println!("n = {N}, mean = {MEAN}, stddev = {STDDEV}");
    for (idx, &cnt) in counts.iter().enumerate() {
        let lb = LOW + width * idx as f64;
        let ub = lb + width;
        let freq = cnt as f64;
        let stars = (freq / peak * MAX_STARS).round() as usize;
        println!("[{lb:+5.2}, {ub:+5.2})  {cnt:5}  {}", "*".repeat(stars));
    }
}
```

Kör vi sen programmet kan det se ut så här:

```shell
$ cargo run --bin plot
   Compiling hello v0.1.0 (/home/jensr/RustroverProjects/simple-project)
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.24s
     Running `target/debug/plot`
n = 10000, mean = 0, stddev = 2
[-8.00, -7.24)      2  
[-7.24, -6.48)      2  
[-6.48, -5.71)     17  
[-5.71, -4.95)     73  **
[-4.95, -4.19)    104  ***
[-4.19, -3.43)    241  ******
[-3.43, -2.67)    466  ************
[-2.67, -1.90)    790  *********************
[-1.90, -1.14)   1119  ******************************
[-1.14, -0.38)   1430  **************************************
[-0.38, +0.38)   1496  ****************************************
[+0.38, +1.14)   1457  ***************************************
[+1.14, +1.90)   1176  *******************************
[+1.90, +2.67)    779  *********************
[+2.67, +3.43)    438  ************
[+3.43, +4.19)    237  ******
[+4.19, +4.95)    104  ***
[+4.95, +5.71)     45  *
[+5.71, +6.48)     12  
[+6.48, +7.24)      6  
[+7.24, +8.00)      6  
$ 
```

* * *

Sammanfattning
==============

Vi har rest en bra bit i den här artikeln: från `rustc hello.rs` och dess `*.o`\-filer, via `make`, `Ant` och `Maven` fram till `cargo`. Längs vägen har vi tittat på hur cargo strukturerar ett projekt (`src/main.rs`, `src/lib.rs`, `src/bin/`), hur moduler byggs upp (fil-baserade, inline, med `#[cfg(...)]`), hur man testar (enhetstest, integrationstest och doctests), hur man genererar dokumentation (`rustdoc`) och slutligen hur man inkorporerar tredjeparts-bibliotek från [crates.io](https://crates.io) — med ett litet demo-program som facit.

Den röda tråden genom hela artikeln är egentligen bara en enda mening, gömd i titeln: **konvention framför konfiguration**. Samma idé som Jason van Zyl gav Java-världen med Maven, har Rust fått med sig från start, inbyggd i själva verktyget. Filnamnet `main.rs` _är_ konfigurationen. Katalogen `src/bin/` _är_ konfigurationen. Det finns i praktiken inget att ställa in för att komma igång — man skapar, kompilerar, testar, dokumenterar och publicerar med samma verktyg, samma kommandon och samma vokabulär, oavsett vilket Rust-projekt man öppnar.

Jämför man med C++, där varje projekt kan ha sin egen kombination av `CMake`/`make`, `Conan`/`vcpkg`/`FetchContent`, `Doxygen` och en handfull hemsnickrade skript, blir skillnaden tydlig. Det är inte det att C++ saknar verktyg — det är att det saknar _ett_ verktyg alla är överens om. Cargo må vara "bara" ett byggverktyg, men det är förmodligen den enskilda komponent som gjort mest för Rusts rykte om att vara ett utvecklarvänligt språk.
