What is the recommended directory structure for a Rust project? - rust

Where should one put the sources, examples, documentation, unit tests, integration tests, license, benchmarks etc?

Cargo, the official package manager for Rust, defines some conventions regarding the layout of a Rust crate:
.
├── Cargo.lock
├── Cargo.toml
├── benches
│ └── large-input.rs
├── examples
│ └── simple.rs
├── src
│ ├── bin
│ │ └── another_executable.rs
│ ├── lib.rs
│ └── main.rs
└── tests
└── some-integration-tests.rs
Cargo.toml and Cargo.lock are stored in the root of your project.
Source code goes in the src directory.
The default library file is src/lib.rs.
The default executable file is src/main.rs.
Other executables can be placed in src/bin/*.rs.
Integration tests go in the tests directory (unit tests go in each file they're testing).
Example executable files go in the examples directory.
Benchmarks go in the benches directory.
These are explained in more detail in the manifest description.
By following this standard layout, you'll be able to use Cargo's commands to build, run and test your project easily. Run cargo new to set up a new executable project or cargo new --lib to set up a new library project.
Additionally, documentation for libraries is often written in documentation comments (comments that start with /// before any item, or //! to document the parent item). Also, the license is usually put at the root.
Unit tests, as mentioned above, are written in the same module as the functions they're testing. Usually, they're put in an inner module. It looks like this (this is what Cargo generates for a new library with cargo new --lib):
#[cfg(test)]
mod tests {
#[test]
fn it_works() {
}
}

Related

Can I have a Cargo workspace with two binaries relying on a library where only one binary enables a feature of the library?

I have a Cargo workspaces project with the structure:
├── src
├── lib
│   └── Cargo.toml
├── tools
│   ├── src/bin/foo.rs
│   └── Cargo.toml
└── Cargo.toml
., lib and tools are my three workspace members. lib has a feature that enables tracing of the execution of a performance-sensitive function. As this feature adds performance overhead, the main binary should not enable it, but the foo binary is a testing tool which uses it.
However, Cargo workspaces take the union of the features of the two binaries, so the main binary ends up including the feature anyway.
How can I fix this?

Workspace optional dependencies (std & no_std)

I am developing a code for my school's rocketry team and I have two programs, one meant to flash the on-board computer and another to run some data analysis on the flight data. The chip code uses no_std while the data analysis program uses std. The data-analysis code will run on my PC, and the chip code will run on the chip.
Here is my workspace root Cargo.toml and my project graph:
[workspace]
members = [
"chip",
"data-analysis",
]
.
├── Cargo.lock
├── Cargo.toml
├── chip
│   ├── Cargo.toml
│   ├── memory.x
│   ├── openocd.cfg
│   ├── openocd.gdb
│   └── src
│   └── main.rs
├── data-analysis
│   ├── Cargo.toml
│   └── src
│   └── main.rs
├── README.md
└── resources
├── 3m.mkd
├── data-stm32f103c8t6.pdf
├── links.txt
├── reference-stm32f103xx.pdf
├── schematic-stm32f103c8t6.png
└── todo.txt
I have decided to use a workspace to organize my code. When I attempt to build the workspace, I get the error:
error[E0463]: can't find crate for `std`
|
= note: the `thumbv7m-none-eabi` target may not support the standard library
= note: `std` is required by `data_analysis` because it does not declare `#![no_std]`
= help: consider building the standard library from source with `cargo build -Zbuild-std`
When I compile with cargo build -Zbuild-std I get the error:
error[E0463]: can't find crate for `panic_abort`
error[E0658]: use of unstable library feature 'restricted_std'
|
= help: add `#![feature(restricted_std)]` to the crate attributes to enable
However I need no_std and not restricted_std.
I understand that dependencies for all files are stored in Cargo.lock and presumably that's why it is producing this error. My question is, how do I express to the compiler that I need std for data_analysis but not for chip code? Should I even be using workspaces over just using one package with multiple binaries and using [features] in the Cargo.toml?
It seems like you're trying to complile data_analysis with the same target as chip. I recommend just getting rid of the root Cargo.toml and compiling the respective ones in the folders. You could make a Makefile to automate this easily

What's the difference between binary and library in Rust? [duplicate]

This question already has an answer here:
What is the difference between library crates and normal crates in Rust?
(1 answer)
Closed 2 years ago.
What's the difference between binary and library in Rust?
I read The Cargo Book, but couldn't understand it well.
I generated two folders using cargo new a --bin and cargo new b --lib, however, both of them look the same inside. What are the purposes of --bin and --lib? And what are the difference between them?
A binary crate should generate an executable (or multiple) that can be installed in the user's path and can be executed as usual.
The purpose of a library crate on the other hand is not to create executables but rather provide functionality for other crates to depend on and use.
Also they do differ in their structure:
✦2 at [22:50:27] ➜ cargo new --bin somebinary
✦2 at [22:50:29] ➜ cargo new --lib somelib
Created library `somelib` package
✦2 at [22:50:34] ➜ tree somebinary/
somebinary/
├── Cargo.toml
└── src
└── main.rs
1 directory, 2 files
✦2 at [22:50:41] ➜ tree somelib/
somelib/
├── Cargo.toml
└── src
└── lib.rs
You can also find more information in this rust-lang forum thread: https://users.rust-lang.org/t/what-is-the-difference-between-cargo-new-lib-and-cargo-new-bin/19009
One creates an src/main.rs and other creates src/lib.rs. They are different in the nature of the files which are created. Differences lies in whether you are interested in creating a library or interested in creating a binary
Are you sure you ran those exact same commands?
(ins)temp->tree
.
├── a
│   ├── Cargo.toml
│   └── src
│   └── main.rs
└── b
├── Cargo.toml
└── src
└── lib.rs

Why is cargo build cache invalidating?

I have a barebones workspace project:
.
├── build-debug.sh
├── Cargo.lock
├── Cargo.toml
├── common
│   ├── Cargo.toml
│   └── src
│   └── lib.rs
├── rs-test.iml
├── server
│   ├── Cargo.toml
│   └── src
│   └── main.rs
└── wui
├── Cargo.toml
└── src
└── lib.rs
The rs files either empty or just an empty main function.
The server and the wui depends on common: common = { path = "../common" }.
The common project has one crates.io dependency with I suppose build script or proc macro dependency.
The build script:
cargo build -p wui --target wasm32-unknown-unknown
cargo build -p server
The problem:
When I rebuild the unchanged project, some wui dependencies are getting invalidated/rebuilt, then the same for server.
Either:
remove the wasm32 target flag
replace the dependency with a simple crate without build time compiled dependencies
It does not rebuild the subprojects anymore.
Is this a cargo bug? What can I do?
It's probably not a cargo bug. What is likely happening here is that your crates.io dependency (you don't mention what it is, which might have been useful) has different dependencies or features depending on the target architecture. Thus, as you alternate between building the WASM target and your host target, stuff is being rebuilt.
Perhaps it would be better in this case to stop using the Cargo workspace and build the server and wui separately; this way you'll have separate target directories for the server and wui, which takes some extra disk space and takes longer for non-incremental compilation, but will prevent you from having to rebuild that stuff all the time as you build both.

How do I tell Cargo to build files other than main.rs?

Here is my directory structure:
lowks#lowkster ~/src/rustlang/gettingrusty $ tree .
.
├── Cargo.lock
├── Cargo.toml
├── foo.txt
├── src
│   ├── boolean_example.rs
│   ├── function_goodbye_world.rs
│   ├── listdir.rs
│   ├── looping.rs
│   ├── main.rs
│   ├── pattern_match.rs
│   └── write_to_file.rs
└── target
├── build
├── deps
├── examples
├── gettingrusty
└── native
6 directories, 11 files
When I run 'cargo build', it seems to only build main.rs. How should I change Cargo.toml to build the rest of the files too?
Put other.rs file into bin subfolder of src folder (./src/bin/other.rs). And run cargo build --bin other or cargo run --bin other
The Rust compiler compiles all the files at the same time to build a crate, which is either an executable or a library. To add files to your crate, add mod items to your crate root (here, main.rs) or to other modules:
mod boolean_example;
mod function_goodbye_world;
mod listdir;
mod looping;
mod pattern_match;
mod write_to_file;
To access items defined in another module from your crate root, you must qualify that item with the module name. For example, if you have a function named foo in module looping, you must refer to it as looping::foo.
You can also add use statements to import names in the module's scope. For example, if you add use looping::foo;, then you can just use foo to refer to looping::foo.
For more information, see Separating Modules into Different Files in The Rust Programming Language.
There are a few different types of binaries or targets that cargo recognizes:
binaries
libraries
benchmarks
integration tests
examples
For example, if the file boolean_example.rs is a standalone example that you want to run you can put in inside an examples directory and tell cargo about it like so:
[[example]]
name = "boolean" # examples/boolean.rs
This lets you invoke your example with cargo run --example boolean
Read the cargo book's page on package layout as well to see how these target directories can be structured.
you can include your testing in the main.rs file as following >>
Filename: src/main.rs
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn this_test_will_pass() {
let value = 4;
assert_eq!(4, value);
}
#[test]
fn this_test_will_fail() {
let value = 8;
assert_eq!(5, value);
}
}
Or call them from your tests file.
then run them using test command: cargo test
from filename: lib/tests.rs
mod tests;
tests::run();
in this case main.rs will be built but only tests.rs file will be
executed.
more prove:

Resources