What are packages, crates and modules? - rust

I have read Managing Growing Projects with Packages, Crates, and Modules, but I still don't really understand the hierarchy. I know what is crate and that there should be at least one crate in package: max 1 library crate and 0 or more binary crates.
First: Assume I want to have both lib.rs and main.rs crates in package. How do I access/call functions from lib.rs in main.rs?
Second: when I create new library with cargo new --lib library-name it creates directory with that name and bunch of files there, the only way I figured out to call functions from that library in src/main.rs is:
mod some_library;
use crate::library_name::library_name::foo;
fn main() {
foo();
}
// Filename: src/library_name.rs
pub mod library_name; // I don't really understand this
// Filename: src/library_name/library_name.rs
pub fn foo() {
// ...
}
where I have the following hierarchy:
- package_name
- src
- library_name
- src
- lib.rs
- Cargo.toml
- library_name.rs
- library_name.rs
- main.rs
- Cargo.toml
Is it necessary for src/library_name.rs to have the same name as src/library_name library? I'm really confused.

It's a hierarchy of three or four levels.
Workspace (optional): At the top there's the workspace. A workspace consists of one or more packages. The Cargo.toml file is special and more or less only lists the workspace members.
Workspaces are optional and used for big projects. Smaller projects with only one package don't need them. In such cases we can ignore workspaces. We have a package at the top and the hierarchy is only three levels deep.
Package: Then there's the package. A package is what haves a real Cargo.toml file. A package can compile to crates.
Crate: A crate is a library or an executable compiled from the package. A library crate has a lib.rs file as a starting point. An executable crate has a main function as a starting point. A package can compile to at most one library and several executables.
Module: Then there are modules. Modules are the hierarchical way how Rust organizes items like struct, enum, functions and others in the source code.
And how to you use this to give items unique names?
Answer: A fully qualified name starts with the package, then a series of module names then finally the item's name.
An example:
serde::de::value::StringDeserializer (see here) has as package serde, as module path de then value then the struct is called StringDeserializer.
And where's the crate?
The package serde has only one crate, the library. If you look at the package's Cargo.toml there are no binaries listed.
(People tend to be confused about packages and crates. Even sometimes I am not precise and say "the serde crate" when I mean the package.)
In short: A package is code under a name (for example serde). A crate is a library or an executable.

Related

Why only main.rs can declare mod?

main.rs
main2.rs
file1.rs
src/something.rs
Cargo.toml
on main.rs, doing mod file1 works, but doing mod file1 on main2.rs leads to
file not found for module `file1`
Why only main.rs can declare sibling files with mod?
New modules can be declared from both main.rs and lib.rs. The first is used for binary crates, the latter - for libraries. It's important to note that if a package has both files, then it will have two crates with the same name (one library and one binary). You can also have multiple binary crates, if you define the crate roots (explained below) in src/bin - each file will be treated as a separate binary crate. This is explained in detail in the rust book.
These two files are special - they are called crate roots, because they form a tree-like structure. Each module should be part of it. You cannot define a module outside that tree. This is again explained in the book.
Thus in order to be able to define a new module from within main2.rs, it either has to be a crate root - i.e. it has to have a main() and be located in /src/bin/main2.rs or it has to be part of the module-tree, descending from some of the crate roots.

How to import diesel models and schema? [duplicate]

This question already has answers here:
How do I do a basic import/include of a function from one module to another in Rust 2015?
(3 answers)
Split a module across several files
(7 answers)
How can I include a module from another file from the same project?
(6 answers)
Closed 5 years ago.
How do I include a file with the full path my_project/src/include_me.rs in main.rs?
I've checked the dependencies guide, and all of them appear to be including a binary. I've also checked "The Book", but none of the examples there end in ".rs" either.
How do I make include_me.rs compile with the rest of the project?
There are basically two (main) ways in Rust to include code from somewhere else:
1. "Including" internal code
If your include_me.rs belongs to your project, you should move it to the same folder main.rs lies in:
└── src
├── include_me.rs
└── main.rs
Then you can write this in your main.rs:
mod include_me;
fn main() {
// Call a function defined in the other file (module)
include_me::some_function();
}
A mod declaration makes the Rust compiler look for the corresponding .rs files automatically!
So everything that belongs to your project, belongs in the same folder (or a subfolder thereof) as the folder where main.rs (or lib.rs) is lying. The files are then "included" via the module system. To read a good introduction into modules, please read the chapter on modules in the Rust book. You could also check out Rust by Example on that topic. The module system is pretty central and thus important to learning Rust.
2. "Including" external code
If your include_me.rs is something that doesn't belong to your actual project, but is rather a collection of useful things you are using in multiple projects, it should be seen as a library. To include code of such external libraries, you have to include it as an extern crate. And to make your life easier, you really want to use Cargo!
So let's prepare your include_me.rs as Cargo library project. You need the following file structure (which is automatically generated by cargo new my_library --lib):
. my_library
  ├── Cargo.toml
  └── src
  └── lib.rs
Now copy all the contents from include_me.rs into lib.rs (it is just convention to call the root file of a library project lib.rs). Let's say that the my_library folder's full path is ~/code/my_library.
Now let's prepare your main project's Cargo project. It has a similar file
structure (but a main.rs instead of lib.rs, because it's a executable-project, not a library-project):
. my_project
├── Cargo.toml
└── src
└── main.rs
To declare your dependency on my_library, you have to put this into your Cargo.toml:
[package]
name = "my_project"
version = "0.1.0"
authors = ["you"]
edition = "2018"
[dependencies]
my_library = { path = "~/code/my_library" }
You can also use relative paths ("../my_library"), but it only makes sense if you know that the two projects always stay where they are, relative to one another (like if they are both managed in the same repository).
Now you can, in your main.rs, write:
use my_library::some_function;
fn main() {
// Call a function defined in the other file (extern crate)
some_function();
}
If you want to upload any of those two projects, you have to interact with crates.io (or another crates registry, if your company has one), but this is another topic.
(Note: some time ago, it was necessary to write extern crate my_library; inside main.rs. This is not necessary anymore, but you might find old code with extern crate declarations.)
Any other ways?
Yes, but you shouldn't use those. There is the include!() macro which allows you to verbatim include other files, just like the #include from C-land. However, it is strongly discouraged to use this in situations where the module system would be able to solve your problem. include!() is only useful in very special situations, often linked to a more complex build system which generates code.

How to make an item visible in my binary crate targets but not any other crates?

I would like to generate multiple binaries using a lot of the same common code. If I write everything in src/main.rs I can simply mark items at pub(crate) and access the code without exporting it. However if I put the binary in src/bin/foo.rs then I can not find a way to access this without marking everything pub. I would not like to mark everything pub not only because I don't want others to depend on it but also because it renders visibility checking ineffective.
The only workaround I have found is to put the file inside of the src directory then put a simple shim in bin/foo-bar.rs that just calls my_crate::bin_foo_bar::main(). This isn't very tidy and requires a bunch of overhead.
Inside your package you may define a single lib crate and multiple binary crates. If you declare a type inside your library crate as pub(crate) it will obviously unvisible from your binary crates. So revise the definitions. A package is not a crate, it is a package of crates. And pub(crate) types are only visible inside the crate they belong to.

Testing a Rust crate outside of the source package

I have created my Rust crate. It's a very trivial affair. It built fine and when tested within it's own source directory works just fine (I just included extern crate my_first_crate; in my test file).
I want to now test the crate in a totally different application.
If I add the same extern crate line to my new application, the compiler tells me that it can't find the crate. I expected this (I'd get the same in C if I told the compiler to link to a library it has no clue about!)
Do I need to copy the my_first_crate.rlib file from the source to the application target/debug folder or is there a way to tell cargo that it needs to link to the rlib file?
You need to add your crate as a dependency for your application. Add this to your application's Cargo.toml:
[dependencies]
my_first_crate = { path = "/path/to/crate" }
"/path/to/crate" is the path to the root of the crate's source (i.e. the directory that contains its Cargo.toml). You can use either a relative or an absolute path (but avoid absolute paths if you intend on publishing your code!).

Figure out code from what module is "use"d in a large rust project (servo)

I'm trying to read the code of servo. As an example, I'm looking at this code in layout_task.rs:
use url::Url;
..and I want to know which code this refers to (the answer is rust-url).
Per the Rust reference §6.1.2.2 Use declarations,
the paths contained in use items are relative to the crate root [...]
It is also possible to use self and super at the beginning of a use item to refer to the current and direct parent modules respectively.
All rules regarding accessing declared modules in use declarations apply to both module declarations and extern crate declarations.
The reference (§5 Crates and source files) does not explicitly say what a "crate root" is, but it does share that:
A crate contains a tree of nested module scopes. The top level of this tree is a module that is anonymous [...] The Rust compiler is always invoked with a single source file as input, and always produces a single output crate. The processing of that source file may result in other source files being loaded as modules.
So it seems that to find the crate root that the current file (layout_task.rs) belongs to, we need to figure out what source file rustc is invoked with when building the crate. With Cargo this is specified in Cargo.toml and defaults to src/lib.rs:
[lib]
path = "src/lib.rs"
In my case, here's Cargo.toml and the lib.rs has:
extern crate url;
pub mod layout_task;
So far so good. To find out what the extern crate refers to, we need to look at Cargo.toml again:
[dependencies.url]
version = "0.2"
The cargo docs claim that "Dependencies from crates.io are not declared with separate sections", but apparently they can be... So we look the package up on crates.io: https://crates.io/crates/url

Resources