Can the project's src/bin directory contain subdirectories?

Hello.
I have just started learning Rust and have been trying out various examples in a practice project to get a feel for the language. For convenience, I have made each example into a standalone executable file. However, there are too many examples, so I would like to organize them into different directories based on their type.

But I found that only files in the src/bin/filename.rs directory can be run, but the srv/bin/dir1/filename.rs file cannot be run and the following error is reported:

\src\bin> cargo.exe run --bin dir1/filename
error: no bin target named `dir1/filename`.
Available bin targets:
...


Is it true that the "rust bin" directory does not support subdirectories?
Since there are too many executables and they need to be organized, how can they be managed better?

This may be a personal habit issue.
When I learn a new language or do other things, I create a learning project and put all the examples I write during my studies in it.
I also test the various functions of third-party libraries I need later in this learning project before using them in my actual projects. I determine that everything is normal and works properly before writing code in my actual projects.
I usually keep the learning project for the language I am using open in my daily coding. If I forget the specific usage of a function or a third-party library in a later formal project, I can simply refer to the examples in the learning project.

One option for making this work would be to define the binary targets in your Cargo.toml and set the path field , rather than relying on them being found at the default location. Collecting together individual executables isn't really what multiple binaries in a package is intended for though (I think the main use case is providing multiple frontends to a library crate defined in the package). In particular, the binary crates will all share the same dependencies.

Another option for organising these executables, if you want to keep them together but in separate packages, would be to define a workspace. That would allow each package to have its own dependencies and settings, while also allowing for some shared dependencies and settings.

2 Likes

You can also use cargo --example

$ cargo run --example hello

but it is similar to --bin.

In your case - many small examples - perhaps better to have just one program, and let each example be a separate function. Use clap to process the command line and pick the function to run...

1 Like

Can you please explain " $ cargo run --example hello" in some more detail. I have learned about --example when using Bevy and Xilem, but found no explanation. Is "example" a special folder name, or how does it work?

I think what you might be looking for is something like Rustling. Here are several methods that can help you manage numerous example executable files better. You can choose to use them according to your own habits and needs.

Method 1: Use the "rust_study" folder combined with "cargo new" to create learning examples

  1. Create the main folder and project structure
    First, create a folder named "rust_study". This folder will serve as the general directory where you store all your learning examples. After entering this folder, you can use the "cargo new" command to create each individual learning example project. For example, if you want to create an example project named "example_1", open the command line in the "rust_study" directory and execute the "cargo new example_1" command. This will generate a new Rust project structure, which is similar to the following:
rust_study
└── example_1
    ├── Cargo.toml
    └── src
        └── main.rs

Each project created by "cargo new" has its own independent "Cargo.toml" file for managing project dependencies and other information, as well as the "src/main.rs" file for writing example code. You can create multiple different example projects in this way, such as "example_2", "example_3", and so on, and put different types of examples in different projects respectively.

  1. Write and run example code
    In the "src/main.rs" file of each project, you can write the corresponding example code. When you want to run a certain example, enter the corresponding project directory (for example, "rust_study/example_1"), and then execute the "cargo run" command in this directory. This will run the executable file in this project, that is, execute the code logic written in "src/main.rs". This way makes each example project independent of each other, with a clear structure, which is convenient for managing and viewing different example contents.

Method 2: Utilize the "bin" directory (with a slight adjustment based on the original project structure)

  1. Adjust the directory structure
    In your existing project (assuming the root directory of your current project has a certain name, such as "rust_project"), you can create a folder named "bin" under the "src" directory, and then sort the numerous independent executable files you had before into different subfolders under the "bin" directory according to their types. For example:
rust_project
└── src
    └── bin
        ├── type_1_examples
        │   ├── example_1.rs
        │   └── example_2.rs
        └── type_2_examples
            ├── example_3.rs
            └── example_4.rs

Here, different types of learning binarys files are placed in different subfolders such as "type_1_examples" and "type_2_examples", which is convenient for classified management.

  1. Modify the "Cargo.toml" file to specify executable files
    Next, you need to specify the path of each executable file in the "Cargo.toml" file in the root directory of the project through the "[[bin]]" table, as shown in the following example:
[[bin]]
name = "001"
path = "src/example/type_1_examples/example_1.rs"

[[bin]]
name = "002"
path = "src/example/type_2_examples/example_3.rs"

In this way, configure the path and corresponding name of each ".rs" file that you want to be an executable file in the "Cargo.toml" file. After that, you can run the corresponding example file by using the command like "cargo run --bin 001" (replace "001" here with the actual file name you configured). This method can also achieve effective management of numerous examples by reasonably organizing the directory and configuring it on the basis of the original project.

Thank you danjl1100 for correcting the mistake

Method 3: Use a workspace

  1. Create a workspace structure
    Create a new folder as the root directory of the workspace, such as "rust_workspace", and then create different sub-project (package) directories and corresponding "Cargo.toml" files in it to store different types of example contents. The structure is probably as follows:
rust_workspace
├── Cargo.toml  (The main configuration file of the workspace)
├── example_package_1
│   ├── Cargo.toml
│   └── src
│       └── main.rs  (Write the example code of the first type here)
└── example_package_2
    ├── Cargo.toml
    └── src
        └── main.rs  (Write the example code of the second type here)
  1. Configure the main "Cargo.toml" file of the workspace
    In the "rust_workspace/Cargo.toml" file, configure the packages included in the workspace, like this:
[workspace]
members = ["example_package_1", "example_package_2"]
  1. Configure the "Cargo.toml" files of each sub-project and write code
    In the "Cargo.toml" file of each sub-project, you can configure project information, dependencies, and other contents as in a regular project, and then write specific example code in the corresponding "src/main.rs" file. For example, write a certain type of example in "example_package_1/src/main.rs" and another type of example in "example_package_2/src/main.rs".

  2. Run and manage example projects
    To run the example in a certain sub-project, just execute the command like "cargo run -p example_package_1" (replace the specific sub-project name after "-p" according to the actual situation) in the root directory of the workspace. The advantage of using a workspace is that it can manage different types of examples in a more modular way. The dependencies of each sub-project can also be relatively independent and shareable, which is convenient for you to continuously expand and organize the learning example contents later.

I hope the above methods can help you manage the numerous example executable files you created during your Rust learning process better. You can choose the appropriate method according to your own usage habits.

4 Likes

That is a great write up, which I have never seen before. Do we have permission to copy that text into our books, perhaps also into the "official" Rust book?

Yes, if you create a folder called "examples" and put your mains in there, e.g. "hello.rs", then you can run them like above

cargo run --example hello

Very similar to putting mains in src/bin. For more complex examples - like in Bevy - there is additional set up in Cargo.toml. gnuplot and crossterm are other examples.

It is mentioned in The Book but very brief.

What I found confusing originally is that sometimes crates have an examples directory, but not based on this cargo option - just complete rust projects dumped in there (e.g. yew)

1 Like

Ofc you can. I knew these tricks from some projects on GitHub. I wholeheartedly agree with breaking down these obscurities for beginers. However, these are just some simple usages. I hope you can conduct further searches and verifications on this basis

1 Like

The top-level directory of yew's repository isn't a crate in itself, rather it is a workspace that contains various Rust packages, some of which are in an examples folder.

Generally speaking, the "examples" is just a folder used to store examples(samples). The reason why you are confused is that sometimes the ways to run them are different. Usually, you can use the command "cargo run --example xxx". However, sometimes the running methods vary. For specific details, please refer to the README file. If you read README of Yew examples, you'll know that they are built with Trunk

Yew Examples

How to Run

The examples are built with trunk. Once you have the development environment fully set up (see documentation), running an example is as easy as running a single command:

# move into the directory of the example you want to run
# In this case it's the todomvc example
cd examples/todomvc

# build and serve the example
trunk serve --open

I think Method 2 missed the reason for using the examples directory at the root of the crate.
See the project layout docs here: Package Layout - The Cargo Book

2 Likes

Sorry for the misunderstanding. What I meant by "examples" is actually used for organizing various binaries. I should have used "bin" instead. Here's the structure:

rust_project
└── src
    └── bin
        ├── type_1_examples
        │   ├── example_1.rs
        │   └── example_2.rs
        └── type_2_examples
            ├── example_3.rs
            └── example_4.rs

btw If you notice that the way to run them is "cargo run --bin xxx" rather than "cargo run --example xxx", you can tell the difference between them

Thank you very much for your help. I will try using the workspace. During actual testing of third-party libraries, I found that different versions of the library had different incompatible APIs, so I may need to use different examples with different library dependencies.

Thank you for your reply, which has given me more knowledge about the related topics. Clap looks like a great command-line library.

Thank you for providing the perfect solution. I will take note of your content. I will now try to implement this using the Workspace, as I found that the API of some libraries is not consistent across different versions, so if I want to test thoroughly, I need to implement code for different versions of the same library.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.