# ===== (0) my_crate/Cargo.toml ===== [package] name = "my_crate" version = "0.1.0" edition = "2021"
[dependencies] serde = { version = "1", features = ["derive"] }
1 2 3 4 5 6 7 8
// ===== (1) my_crate/src/lib.rs ===== mod config; // declare child — REQUIRED, or config.rs is dead mod database; // declare child — REQUIRED, or database/ is dead mod network; // declare child — REQUIRED, or network/ is dead
Crates are published and distributed as source code (.rs files). Cargo downloads the source and compiles it on your machine.
Python
Rust
Wheels (.whl) — pre-compiled binaries (optional C extensions)
Everything compiles from source
pip install fetches best wheel, falls back to source tarball
cargo build always compiles from source
Dynamic linking common for native code
Static linking is the default
This is like C++ templates (which must be in headers to compile everywhere), except in Rust it is the default for everything. The upside: trivial cross-compilation and no ABI issues. The downside: longer initial compile times.
Version Resolution — The Killer Feature
Rust’s approach to dependency conflicts is fundamentally different from Python’s.
Python: Flat Namespace
1 2 3
requests==2.28.0 some-lib → requests==2.31.0 # 💥 pip: "conflict!" — you must resolve by hand
site-packages/ is flat. Only one version of requests can exist. Conflicts are your problem.
Rust: Multi-Version Coexistence
Scenario
Cargo’s behavior
Same major version (e.g., serde 1.0.180 and serde 1.0.210)
Picks the highest compatible version. Compiled once.
Different major versions (e.g., serde 0.9 and serde 1.0)
Compiles both into the binary. No conflict, no error.
Irresolvable (no version satisfies all constraints)
Clear build error. Extremely rare in practice.
Import Paths
Python
Rust
from pkg.db.models import User
use crate::db::models::User;
from . import sibling
use super::sibling;
. path separator
:: path separator
Package-root-relative by default
Module-relative by default (crate:: for absolute)
Important: In Rust, a bare path like use database::connect; is resolved relative to the current module, not the crate root. Always use crate:: for absolute paths:
1 2 3
// Inside database/mod.rs use models::User; // relative → crate::database::models (correct: sibling) use crate::config::Settings; // absolute → crate::config (unambiguous)
Publishing a Crate
Step
Python
Rust
Register
PyPI account
crates.io account (login with GitHub)
API token
PyPI token
crates.io token → cargo login
Package
python -m build
cargo package
Dry run
—
cargo publish --dry-run
Upload
twine upload dist/*
cargo publish
Immutability
Versions are immutable
Versions are immutable (can only yank)
Required metadata in Cargo.toml:
1 2 3 4 5 6 7 8 9 10
[package] name = "my-crate" version = "0.1.0" edition = "2021" description = "A short description of what it does" license = "MIT OR Apache-2.0" repository = "https://github.com/you/my-crate" readme = "README.md" keywords = ["some", "relevant", "tags"] categories = ["category-slug"]