Alright, let’s get our hands dirty. Before you can build anything, you need to tell Cargo what you’re building. Is it a library? A binary? A terrifyingly complex workspace with a dozen sub-crates? This is where type and layout come in, and they are the bedrock of your Cargo.toml. Get these wrong, and nothing works. Get them right, and you’ve laid a solid foundation.

The package.type Field: What Are You, Anyway?

The type field, specified under the [package] table, is the single most important piece of information you’ll declare. It tells the Rust compiler and Cargo the fundamental nature of your project’s output. Forget this, and Cargo will make an assumption—an assumption you probably won’t like.

For 99% of you, there are only two values you’ll ever need: "bin" and "lib".

  • type = "bin": This means “I am building an executable.” You know, a program you can run from the command line. If you’re writing a CLI tool, a game, or a web server, this is you. Here’s the fun part: This is the default. If you don’t specify a type at all, Cargo assumes you’re building a binary. So, for most simple binary projects, you can just omit this field entirely. See? The system isn’t all terrible.

    [package]
    name = "my-cool-cli-tool"
    version = "0.1.0"
    # type = "bin"  # This is implied! You don't even need to write it.
    
  • type = "lib": This means “I am building a library.” Your crate’s purpose is to be used as a dependency by other crates. It exposes modules, functions, and types for others to use. When you build this, you get an .rlib file (among other things) instead of an executable. If you’re making a library, you must declare this. The default is "bin", remember? If you leave it out, Cargo will get confused looking for a main.rs that doesn’t exist and throw a fit.

    [package]
    name = "my-awesome-library"
    version = "0.1.0"
    type = "lib"  # Crucial! Now Cargo knows to look for lib.rs.
    

Now, for the 1% of you building things like static libraries (staticlib) or dynamic libraries (cdylib) to integrate with other languages like C or Python, you’ll need to specify that here. But for the rest of us, bin or lib is the entire universe.

The package.layout Field: A Ghost in the Machine

Here’s where we venture into the arcane. You might go your entire Rust career without touching this. The layout field is not something you’ll find in the official Cargo book anymore because, frankly, it’s unstable and intended for building Cargo itself. It’s a power-user feature for when you need to completely override Cargo’s standard directory structure.

Why would you do this? Almost never. But if you’re integrating Cargo into a larger, existing build system that has its own rigid opinions on where source should live, you might use it to point Cargo at your own bizarre directory layout. You define it in a Cargo.toml like this, specifying the path to a directory that contains your custom structure definition:

[package]
name = "my-custom-monstrosity"
version = "0.1.0"
layout = "build/cargo-layout.toml"

Inside that cargo-layout.toml, you’d have to define every single directory path Cargo expects to use. It’s a recipe for pain and something you should avoid unless you have a very specific, masochistic need. The fact that it exists is a testament to Cargo’s flexibility, but consider it a fire alarm behind glass: break it only in case of a genuine emergency.

The Unspoken Default: The src/ Directory Convention

While layout lets you override it, the real magic is in the convention. Cargo has a strong, sensible opinion on where your code lives: the src/ directory.

  • For a binary (type = "bin" or default): Cargo must find a src/main.rs file. This file contains your fn main() entry point. It’s non-negotiable.
  • For a library (type = "lib"): Cargo must find a src/lib.rs file. This is the root of your library’s module tree.

This is why you can often omit the type field for binaries—Cargo finds src/main.rs and just knows. This convention-over-configuration approach is what makes simple Cargo projects so blissfully easy to set up. You don’t need to configure the paths; you just put the files where they’re supposed to go. It’s one of the few places in systems programming that feels genuinely elegant. So just follow the convention. Your future self, who isn’t thinking about build systems, will thank you.