---
title: 'Setup and the Package Manager'
source: 'https://academia.sh/en/courses/rust-fundamentals/setup-and-the-package-manager'
course: 'Rust Fundamentals'
language: en
updated: '2026-09-15T04:58:45+00:00'
license: 'CC BY-SA 4.0'
objectives:
    - 'Lists the three parts required for a source to run (entry point, crate root, manifest)'
    - 'Shows that these three parts fall into the item class, and that their use, not the parts themselves, produces a value'
    - "Proves that the manifest stands outside the source by changing the output's name without changing the source"
---

# Setup and the Package Manager

There are three parts a source is required to write for it to turn into a runnable binary — the entry point, the crate root, the manifest; all three are items, none of them produces a value on its own, but the three together produce a single output, and a change in the manifest can change that output's name without ever touching the source.

The previous lesson defined the three classes and split eighteen writings among them; none of
them alone was sufficient for a program to run, because the question asked was already a
different one. This lesson narrows the question: what, at minimum, must a source text write,
with nothing missing, for it to turn into a running binary? The answer is three parts, and all
three fall into a class familiar from the previous lesson — item. None of them produces a
value on its own, but the three together produce a single output: an executable program.

This question looks smaller than the previous lesson's question, but it is not independent of
it. The previous lesson asked what something written in the source produces when evaluated;
this lesson asks what frame must stand around the source for evaluation to even begin. The
expression, statement, and item distinction is a classification valid only inside Rust source;
this lesson shows where that source begins and its relationship to the data surrounding it.

## Three Mandatory Items

The three parts required for a source to turn into a runnable binary are: the **entry point**
(the `fn main` marking where the program will start), the **crate root** (the file itself
where the compiler starts reading), and the **manifest** (the data file holding the package's
name and which language edition it compiles with). A standard project layout separates these
as follows:

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

# Cargo.toml contents
[package]
name = "calculator"
version = "0.1.0"
edition = "2021"
```

These three fall into the item class established in the previous lesson: none of them is
evaluated on its own, each only declares something. `fn main` itself is a function declaration
— just like `double` in the previous lesson. The crate root is a file; the file itself is not
"run," only the declarations inside it are introduced to the compiler. The manifest, in turn,
is not even Rust source; it is data in TOML format, and it is read by the package manager.

The separation in the directory tree is not arbitrary: only source files sit under `src/`,
while `Cargo.toml` sits at the project's root, outside `src/`. This layout keeps "source" and
"data about the source" out of the same folder — a tool scanning all source files only needs
`src/`, and reads the manifest with separate logic. Here we use a single-file source and hand
it directly to the compiler, because the package manager is never run in this course. In a
real project, the crate root is `src/main.rs`, and the compiler starts there and reaches other
files through `mod` declarations — that expansion is the subject of the Modules and Crates
lesson; here it is enough to know the root is a single file and compilation starts from there.

**LB4.** `fn main` being "used" differs from a function being used in the previous lesson:
`double` was called from within the program itself, while `main` is called by no line at all —
what calls it is the runtime environment that takes over when the operating system runs the
compiled binary at the start of a process. The declaration (item) is the same declaration, but
the calling side is outside the source.

This does not mean the source is incomplete on its own; the record is complete at the crate
root, only the source cannot decide at what moment that record turns into a value. A source
can also contain other items, such as `double` or `observe` — all of them stand as
declarations, none runs on its own. Only one carries the name `main` and is recognized by the
operating system as the entry point; the others produce a value only if reached from `main`'s
body or from another item `main` calls. This is the project-level counterpart of the previous
lesson's finding that items are valueless in measurement: a source tree can declare as many
items as it likes, but an item unreachable from the entry point never runs.

**LB5.** The manifest is not Rust source at all, so the expression/statement/item
classification does not truly apply to it — that classification is defined only for Rust
source. But it lands functionally in the same place: the manifest itself is not "run" either,
it only supplies data the package manager reads, and it contributes its name to the final
output. The manifest's own format makes this difference visible too: `name = "calculator"` is
a key-value pairing, not a call or a branch — there is no code path to evaluate in TOML, only
data to read.

The whole these three parts form together is called a **package**: the source tree the
manifest points to, together with the manifest itself, forms a single unit of distribution and
compilation. The package manager's name comes from here too — what it manages is not a single
source file, but the whole that source and identity form together. A package's boundary is the
directory in the file system where the manifest is found; every source file under that
directory counts as belonging to the same package.

TOML itself is a data language designed to support this distinction: it contains only
`key = value` lines and section headers opened with square brackets (such as `[package]`).
There is no branching, loop, or function call — a manifest is not "run," only read straight
through as a plain set of records. This stands in direct contrast to Rust source: the source
has expressions and statements to evaluate, the manifest has only data.

The difference between the crate root and the entry point is a similar distinction and should
not be confused. The crate root is a file (where the compiler starts reading); the entry point
is a specific item inside that file (`fn main`, the function where execution will start). In a
small program the two can sit in the same file, but conceptually they are separate: a crate
root can carry helper declarations linked to other files through `mod`, with no `fn main` in
it at all — we saw this in the previous lesson with `mod measure`. What a package needs to be
runnable is not the crate root itself, but `fn main` being found somewhere in the source tree.

The directory tree and the manifest text block shown throughout this lesson are not run and
carry no numeric claim; they only document what a real project looks like. Every example that
runs for the rest of the course will be set up to work directly with the compiler, independent
of the package manager — this is a consequence of setting up a measurement environment that
requires no network access, not a limitation of the language itself.

To see what the entry point does on its own, the source below runs it in its smallest form.

```rust
// our_program.rs — the least a source must write for it to be able to run
fn main() {
    println!("verification-key={}", 7 * 6);
}
```

```
verification-key=42
```

There is a single item in this source: `fn main`. The source itself produces no value — it
tells the compiler "the program's start is here." The value arises when the program is
**run**, in a way similar to how the previous lesson's `double(3)` call's value arose when the
function was **called**. The difference is that the calling side is outside the source.

The print call inside the body itself falls into a class familiar from the previous lesson: a
statement. The previous lesson's table showed exactly this in its "the value of printing" row
— printing returns `()`, and produces no value itself. The only thing that changes here is
that this statement no longer stands alone, but inside the body of an item named `fn main`:
the item itself produces no value, but when the statement in its body runs, it produces a side
effect (a line printed to the screen). The expression, statement, and item classes can nest
this way — an item's body can be made of statements and expressions, but the item itself still
remains an item.

The crate root being the single file the compiler starts reading is a requirement: a file
without `fn main` can be compiled and written to disk (the `measure` function inside `mod` in
the previous lesson did exactly this), but an executable the operating system can run directly
needs an `fn main` somewhere. This is a single fact that will not appear again for the rest of
the course: a source collection with no entry point can be compiled, but it is not runnable.

The separation of these three mandatory parts from one another is not a coincidence; it is
itself a design decision — it stands on the same axis as the course's title. Separating the
source from the source's identity (name, version, language edition) lets the two change
independently: a project's name can change without its logic ever changing, or its logic can
change without its name ever changing. A design that kept both pieces of information in the
same file could not provide this — every tool reading the source would first have to know
where in the source to look for identity information.

## Limiting Measurement: The Manifest Is Not Inside the Source

The manifest standing separate from the source has a concrete consequence: you can change the
output's **name** without ever changing the source, only by changing a field in the manifest.
In a normal project, the package manager derives the binary's name from the manifest's `name`
field. The measurement below shows the same principle, without running the package manager, directly
with the compiler's output-naming option — the source (`our_program.rs`) never changes, only the target
name changes.

```bash
name_a="calculator"
name_b="calc-tool"
rustc --edition 2021 -o "$name_a" our_program.rs
rustc --edition 2021 -o "$name_b" our_program.rs
echo "output_a: $(./$name_a)"
echo "output_b: $(./$name_b)"
if [ "$name_a" != "$name_b" ]; then
  echo "binary_names_differ: yes"
else
  echo "binary_names_differ: no"
fi
```

```
output_a: verification-key=42
output_b: verification-key=42
binary_names_differ: yes
```

**LB6.** This measurement does not run the package manager — it is not run in any code block
in this lesson either. What is shown is the **principle** of the package manager deriving the
binary's name from a data field: `rustc`'s `-o` option stands in here for the manifest's
`name` field. The same source file was compiled twice, with two different target names, and
both gave the exact same output; the only thing that changed was the binary's name.

This result belongs to the same family as the previous lesson's "class is read from the
signature" finding: there, a method's class was determined by looking at its signature, not
its call site; here, a binary's name is determined by looking at the manifest, not the source.
In both cases the source itself does not carry a piece of information; that information sits
somewhere outside the source, and the source runs unaware of it. The `our_program.rs` file
does not know, in any of its lines, that it will turn into a binary named "calculator" or
"calc-tool."

The `output_a` and `output_b` lines in the measurement being exactly identical is itself an
observation: when the same source is compiled twice with the same compiler options, it
produces two separate binaries, but their behavior never diverges. This is the
compilation-level counterpart of the previous lesson's determinism finding — there it was
shown that the same source produces the same eighteen lines on every run, here it is shown
that the same source produces the same behavior on every compilation. The only thing that
changes is a name left outside the compilation.

This independence is not one-directional. Once compilation is complete, the produced binary
becomes independent of the source as well — even if the source file is deleted, a previously
produced binary keeps working.

```bash
rustc --edition 2021 -o binary_example our_program.rs
mv our_program.rs our_program_backup.rs
echo "running: $(./binary_example)"
if [ -f our_program.rs ]; then
  echo "source_still_same_name: yes"
else
  echo "source_still_same_name: no"
fi
```

```
running: verification-key=42
source_still_same_name: no
```

**LB7.** The binary is the **result** of compilation; it is not the source itself, but the
source's evaluated state at a specific moment. Even if the source file is moved or deleted,
the binary does not know it, because the link was established at compile time, and from that
moment on the binary is a file on its own. This shows the ultimate purpose of the
entry-point–crate-root–manifest trio: the three together produce a single, self-sufficient
output independent of the source. This is the foundation of a situation commonly encountered
in deployment practice: only the compiled binary is moved to a server, the source never goes
there — because the binary can run on its own, this move carries no drawback.

This distinction has a concrete practical value: a source file carrying the same computation
logic can be compiled under the name "calculator" as an internal tool in one context, and
under a completely different name as part of a distributed package in another. Two separate
identities are obtained without the source ever being copied or edited, only by changing the
manifest around it — the measurement above shows exactly this: `our_program.rs` was compiled
under two different names without ever being opened and edited even once.

This distinction has a practical consequence: renaming a project can be done without changing
a single character in the source text. The crate root and the manifest carry two separate
layers of visibility for the same project — one tells what runs, the other tells how the
project is named and which language edition it compiles with. The two layers do not mix: you
can read the source without reading the manifest, read the manifest without reading the
source, and neither assumes anything about the other.

The same distinction also holds in reverse: the same crate root can be part of two separate
projects with two separate manifests. What determines which project a source file belongs to
is not the file's content, but which manifest points to it. As seen in the `our_program.rs`
example, this is a consequence of the source's portability: the file's content stays fixed
while the manifest around it can change, and these two changes are independent of one another.

The other fields the manifest carries (the version and language-edition lines seen in the
example above) follow the same principle: they sit in a code block and never mix into the
source itself. In this lesson, the `--edition 2021` option was given directly to the compiler
because the package manager is never run; in a real project, the package manager reads this
information from the manifest and passes it to the compiler itself. The source file carries
none of this information — it only knows what it does, not what the manifest around it
carries.

This lesson showed that the previous lesson's "item" class is not limited to a single
declaration line. A file itself (the crate root) and a project's identity (the manifest) can
be read by the same logic too: neither produces a value on its own, both only set up a frame,
and the code running inside that frame produces an output. The next lesson descends into this
frame, into the entry point's body, and takes up that most of the first lines written there
bind a value to a name.

## Summary

- Three parts are mandatory for a source to turn into a runnable binary: the entry point, the
  crate root, the manifest.
- All three are items: none of them produces a value itself, and the three together produce a
  single output — a running program.
- The side that "uses" the entry point is not the source itself, but the operating system's
  moment of starting a process.
- The manifest is not Rust source and stands outside the source; the output's name can be
  changed by changing only the manifest, without ever changing the source.
- The crate root and the manifest are two separate layers unaware of each other: one carries
  what runs, the other carries how the project is named.
- Once produced, a compiled binary becomes independent of the source: even if the source file
  is deleted or moved, a previously produced binary keeps working.

## Next Step

A body can now be written inside the entry point, but the first line of that body is usually
to establish a name — to bind a value to a name. The next lesson asks which class this binding
itself falls into, and shows by running it the difference between the same name being rebound
three times and being bound once and changed twice — even though both reach the same result.
