# One cargo binary, seven chapters, and a Candle port of Build an LLM From Scratch

> nerdai/llms-from-scratch-rs translates the PyTorch code from Sebastian Raschka's book into Rust on top of the Candle framework. Every example, exercise and listing is reachable from a single cargo subcommand, and the build pulls three framework crates straight from a git URL.

**nerdai/llms-from-scratch-rs** — A comprehensive Rust translation of the code from Sebastian Raschka's Build an LLM from Scratch book.

- Repository: https://github.com/nerdai/llms-from-scratch-rs
- Website: https://www.amazon.com/Build-Large-Language-Model-Scratch/dp/1633437167
- Stars: 336 · Forks: 42
- Language: Rust
- License: MIT
- Published: 2026-09-17 · Updated: 2026-09-17 · Language: en
- Canonical page: https://hysenlabs.com/projects/nerdai-llms-from-scratch-rs

## Examples and exercises differ by one word and by their numbering

The repository ships a single binary whose command line is parsed by clap 4.6.1 with the derive feature, so moving between a worked example and an exercise costs one word at the prompt. Numbering is not uniform between the two. Examples use two-part zero-padded identifiers such as 02.01 and 05.07, while exercises use a single decimal such as 2.1 and 5.5. The list subcommand prints a bordered table, and comfy-table 8.0.0 sits in the dependency list to render it.

The examples table has an Id column and a Description column. Row 02.01 is described as an example usage of `listings::ch02::sample_read_text`, which means the description field doubles as a pointer into the source tree, and row 02.02 is described as using candle to generate an embedding layer. The exercises table swaps Description for Statement, and a statement wraps across several rows because the text is longer than the column. Statement 2.1 is titled byte pair encoding of unknown words and asks you to try the BPE tokenizer from the tiktoken library on the unknown words 'Akwirw ier', print the individual token IDs, and then call the tokenizer again. Both listings are printed from the same binary you use to run the items, so finding the number you are blocked on does not require searching the tree.

```sh
# Run code for Example 05.07
cargo run example 05.07

# Run code for Exercise 5.5
cargo run exercise 5.5
```

## Candle arrives as three git dependencies, not registry versions

The framework is the reason this repository exists, because the code printed in the book is PyTorch and every snippet here is that PyTorch translated onto the Candle crate, which the project calls a minimalist ML framework. The manifest does not take Candle from crates.io. It writes candle-core, candle-datasets and candle-nn as a git source pointing at https://github.com/huggingface/candle.git, each carrying the version string 0.9.1 alongside that source.

Two consequences follow before any model code runs. A build needs git plus network access to github.com in addition to the crates.io registry, so a machine behind a proxy or an air-gapped build stops during dependency resolution rather than during training. And because a git source moves while you watch it, the Cargo.lock file committed at the top of the repository is what pins the actual Candle commit for everyone who clones. A build that ignores the lock file resolves against whatever sits on the Candle main branch at that moment, which is a different situation from the twenty-odd other dependencies in the list, all of which are pinned to registry releases.

candle-datasets is declared next to the tensor and neural network crates, so dataset plumbing is part of the same framework surface as the model code rather than a separate loader bolted on beside it.

## The cuda feature forwards to two crates and skips candle-datasets

GPU support is a single Cargo feature, short enough to read in full: `cuda = ["candle-core/cuda", "candle-nn/cuda"]`. The forward chain covers the tensor crate and the layer crate. candle-datasets is not in that list, so enabling the feature turns on accelerated kernels for the parts that do arithmetic on tensors and layers while dataset code compiles without it.

Nothing else appears under the features heading. There is no second flag to trim the build, no way to name an alternative backend, and no CPU-versus-GPU switch outside this one toggle. On a cuda-enabled device the flag goes on the same run command rather than into an environment variable or a config file:

```sh
# Run code for Example 05.07
cargo run --features cuda example 05.07

# Run code for Exercise 5.5
cargo run --features cuda exercise 5.5
```

Because the choice is made per invocation, running the same example again without the flag compiles a different set of dependencies, and cargo rebuilds rather than reusing the previous artifacts. Expect the first cuda-enabled run on a new machine to be slow for that reason alone, before any dataset is read.

## Imports live inside main() so a file matches the printed listing

One convention is broken on purpose, and the project says so. A note states that the import style used in all of the `examples` and `exercises` modules is not by convention, and that relevant imports are made under the `main()` method of every `Example` and `Exercise` implementation. The reason given is educational: a reader working through the book should know precisely which imports the example or exercise at hand needs.

For a reader that is a real gain. Opening one numbered item shows its entire dependency set on one screen, in the order a printed listing would present it, and nothing has to be traced back to a module header to work out what the file touches. For anyone maintaining the crate it is recurring noise, since every file repeats its own import block instead of sharing one, and the arrangement is precisely what a Rust linter is trained to object to. That tension is visible in the repository's own tooling, which runs clippy through pre-commit.

Navigation is offered two ways: read the cloned repository in an IDE, or read the generated API documentation on docs.rs under the llms_from_scratch_rs path. The docs route is the one that works without a book open, and it is the reason the crate is published to crates.io at all rather than kept private to the clone workflow.

## The dataset is one wget line whose owner differs from the book link

The second chapter needs text, and the project asks you to fetch it yourself. Two commands create the directory and pull the book's text file into it:

```sh
mkdir -p 'data/'
wget 'https://raw.githubusercontent.com/rabst/LLMs-from-scratch/main/ch02/01_main-chapter-code/the-verdict.txt' -O 'data/the-verdict.txt'
```

Two details in that wget line deserve to be copied exactly rather than corrected from memory. The owner in the raw URL is `rabst`, while the link to the book repository elsewhere on the same page reads `rasbt/LLMs-from-scratch`, so the two paths differ by one letter and one of them is not the spelling used everywhere else. And the file has to land as data/the-verdict.txt, which is the name the examples and exercises look for inside the directory.

The manifest also excludes data/* from the package, so this corpus never travels with an installed crate. The data directory listed at the top of the repository tree is the place the examples expect to find it, and nothing in the listed commands creates it beyond the mkdir line above. Projects that need a different text file have to place it there under that name, since the commands shown cover one file from chapter 2 and nothing else.

## The crates.io path repeats cargo run and misspells the subcommand

An alternative install path exists, and the commands printed for it deserve a careful read rather than a copy-paste. The crate can be installed once Rust and Cargo are on the machine:

```sh
cargo install llms-from-scratch-rs
```

The run commands that follow are the same two used in the clone workflow, and the second one is spelled `cargo run exercsise 5.5` rather than exercise:

```sh
# Run code for Example 05.07
cargo run example 05.07

# Run code for Exercise 5.5
cargo run exercsise 5.5
```

Two things follow from what is printed there. The exercise subcommand name is misspelled in the installed-crate section, so a reader copying those lines in order hits a failure before reaching chapter 5, while the identical command in the clone section is spelled correctly. And both lines still begin with cargo run, which resolves against a package in the current directory rather than naming the executable that the install step just placed on the path, so the difference between the two routes never appears in the commands themselves. Treat the block as a statement of intent: the install gets the binary, and the working directory still decides what the run touches. The project's own preference, stated before either route, is to clone the repository and use Cargo.

## Makefile help strings name Python tools while the recipes run Rust ones

A Makefile at the top level exposes five targets, and its help strings do not match what the recipes do. The help target greps the file for target lines carrying a `##` comment and prints them in yellow with a thirty-column field. The format target is labelled as running code autoformatters (black) and then runs `pre-commit install` followed by `pre-commit run fmt`. The lint target is labelled as running linters, naming pre-commit with black, ruff and codespell, and mypy, while its recipe runs clippy:

```sh
lint:	## Run linters: pre-commit (black, ruff, codespell) and mypy
	pre-commit install && pre-commit run clippy
```

Those labels read like they were carried across from a Python project, since black, ruff and mypy are not the tools a Rust crate is linted with; the commands underneath are the Rust ones, and the pre-commit configuration they rely on is committed as .pre-commit-config.yaml. The check target carries no help comment at all, which makes it the one target missing from the help output even though its recipe runs `pre-commit run cargo-check`. Only the test target bypasses pre-commit and calls cargo directly, and rstest 0.26.1 is the test framework declared among the dependencies to serve it.

## Version 0.1.5 with a release line that stopped in June 2025

The manifest describes a small project with a long tail. It declares llms-from-scratch-rs at version 0.1.5 on edition 2021, MIT licensed, with one named author, Val Andrei Fajardo, and the keywords machine-learning, llms and gpt filed under the science category. Three releases are published: v0.1.3 on 2025-01-23, v0.1.4 on 2025-02-27, and v0.1.5 on 2025-06-05. The gaps run about five weeks, then about three months, and then the release line stops.

The main branch tells a different story, with a push recorded on 2026-09-29 and the repository not archived, so the work continues while the published version does not move. The gap between those dates has a practical consequence: because the manifest still says 0.1.5, anything committed after June 2025 exists only in the repository and never reaches the crate that the install step fetches. Reading the current state of this port means cloning it, which is also the route the project puts first.

Two other top-level entries shape what a reader can expect. CHANGELOG.md and CONTRIBUTING.md sit beside the manifest, and the homepage field points at the Amazon listing for the book rather than at any page of its own, so there is no separate project site collecting decisions or explanations. The repository is the whole of the record: seven chapters of numbered items, a data directory you fill yourself, and a release history that stopped before the last push did.

## Conclusion

Use the clone and cargo run path if you are working through the book in Rust and want each numbered item to run as its own program, because the crate published to crates.io is still stamped 0.1.5 from June 2025 while the main branch has moved on since. Before you start, check three things: that your network can reach the candle repository on GitHub as well as crates.io, that the wget line for the verdict text file still resolves given the owner string there differs by one letter from the book repository link, and that a GPU run really passes the cuda flag, which candle-datasets does not join.

## FAQ

### How are LLMs built from scratch?

The book this port follows splits the work into seven chapters: understanding large language models, working with text data, coding attention mechanisms, implementing a GPT model to generate text, pretraining on unlabeled data, fine-tuning for classification, and fine-tuning to follow instructions. Each chapter maps to numbered examples and exercises that you run one at a time from the same binary.

### How do I build an LLM from scratch?

In this Rust port you clone the repository, download the book's text data into the data directory, and run a single numbered item per command with cargo run example 05.07 or cargo run exercise 5.5. The code printed in the book is PyTorch, and the translation puts it on the Candle framework.

### What does llms-from-scratch-rs change about Sebastian Raschka's PyTorch code?

The stated goal is Rust code that follows the book, with the PyTorch code translated onto the Candle crate, which the project calls a minimalist ML framework. Imports are placed under the main method of every example and exercise so a reader can see which imports one numbered item needs.

### Does the cuda flag cover the whole Candle stack?

No. The cuda feature forwards to candle-core and candle-nn only, and candle-datasets is not part of that list. You turn it on per run with cargo run --features cuda example 05.07 rather than through a config file or an environment variable.

### Should I install the crate from crates.io or clone the repository?

The project recommends cloning and using Cargo to run the examples and exercises, and cargo install llms-from-scratch-rs is offered as an alternative. The published crate is version 0.1.5, released 2025-06-05, while the main branch received a push on 2026-09-29, so later work is in the repository only.

## Sources

- [License: MIT](https://github.com/nerdai/llms-from-scratch-rs/blob/main/LICENSE)
- [nerdai/llms-from-scratch-rs on GitHub](https://github.com/nerdai/llms-from-scratch-rs)
- [Project website](https://www.amazon.com/Build-Large-Language-Model-Scratch/dp/1633437167)
- [README](https://github.com/nerdai/llms-from-scratch-rs/blob/main/README.md)
- [Releases](https://github.com/nerdai/llms-from-scratch-rs/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/nerdai-llms-from-scratch-rs
