-
Notifications
You must be signed in to change notification settings - Fork 13.3k
New Guide: crates and modules #15956
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1770,7 +1770,7 @@ fn main() { | |
|
||
```{notrust,ignore} | ||
$ cargo build | ||
Compiling guessing_game v0.1.0 (file:/home/steve/tmp/guessing_game) | ||
Compiling guessing_game v0.1.0 (file:/home/you/projects/guessing_game) | ||
$ | ||
``` | ||
|
||
|
@@ -2042,7 +2042,7 @@ Let's try it out! | |
|
||
```{notrust,ignore} | ||
$ cargo build | ||
Compiling guessing_game v0.1.0 (file:/home/steve/tmp/guessing_game) | ||
Compiling guessing_game v0.1.0 (file:/home/you/projects/guessing_game) | ||
src/guessing_game.rs:22:15: 22:24 error: mismatched types: expected `uint` but found `core::option::Option<uint>` (expected uint but found enum core::option::Option) | ||
src/guessing_game.rs:22 match cmp(input_num, secret_number) { | ||
^~~~~~~~~ | ||
|
@@ -2246,7 +2246,7 @@ that `return`? If we give a non-number answer, we'll `return` and quit. Observe: | |
```{notrust,ignore} | ||
$ cargo build | ||
Compiling guessing_game v0.1.0 (file:/home/you/projects/guessing_game) | ||
steve@computer:~/tmp/guessing_game$ ./target/guessing_game | ||
$ ./target/guessing_game | ||
Guess the number! | ||
The secret number is: 59 | ||
Please input your guess. | ||
|
@@ -2462,19 +2462,361 @@ rest of your Rust education. | |
Now that you're an expert at the basics, it's time to learn about some of | ||
Rust's more unique features. | ||
|
||
# iterators | ||
# Crates and Modules | ||
|
||
# Lambdas | ||
Rust features a strong module system, but it works a bit differently than in | ||
other programming languages. Rust's module system has two main components: | ||
**crate**s, and **module**s. | ||
|
||
A crate is Rust's unit of independent compilation. Rust always compiles one | ||
crate at a time, producing either a library or an executable. However, executables | ||
usually depend on libraries, and many libraries depend on other libraries as well. | ||
To support this, crates can depend on other crates. | ||
|
||
Each crate contains a hierarchy of modules. This tree starts off with a single | ||
module, called the **crate root**. Within the crate root, we can declare other | ||
modules, which can contain other modules, as deeply as you'd like. | ||
|
||
Note that we haven't mentioned anything about files yet. Rust does not impose a | ||
particular relationship between your filesystem structure and your module | ||
structure. That said, there is a conventional approach to how Rust looks for | ||
modules on the file system, but it's also overrideable. | ||
|
||
Enough talk, let's build something! Let's make a new project called `modules`. | ||
|
||
```{bash,ignore} | ||
$ cd ~/projects | ||
$ mkdir modules | ||
$ cd modules | ||
$ mkdir src | ||
``` | ||
|
||
We need to make our two 'hello world' files. In `src/main.rs`: | ||
|
||
```{rust} | ||
fn main() { | ||
println!("Hello, world!"); | ||
} | ||
``` | ||
|
||
And in `Cargo.toml`: | ||
|
||
```{notrust,ignore} | ||
[package] | ||
|
||
name = "modules" | ||
version = "0.1.0" | ||
authors = [ "[email protected]" ] | ||
``` | ||
|
||
Let's double check our work by compiling: | ||
|
||
```{bash,ignore} | ||
$ cargo build | ||
Compiling modules v0.1.0 (file:/home/you/projects/modules) | ||
$ ./target/modules | ||
Hello, world! | ||
``` | ||
|
||
Excellent! So, we already have a single crate here: our `src/main.rs` is a crate. | ||
Everything in that file is in the crate root. A crate that generates an executable | ||
defines a `main` function inside its root, as we've done here. | ||
|
||
Let's define a new module inside our crate. Edit `src/main.rs` to look | ||
like this: | ||
|
||
``` | ||
fn main() { | ||
println!("Hello, world!"); | ||
} | ||
|
||
mod hello { | ||
fn print_hello() { | ||
println!("Hello, world!"); | ||
} | ||
} | ||
``` | ||
|
||
We now have a module named `hello` inside of our crate root. Modules use | ||
`snake_case` naming, like functions and variable bindings. | ||
|
||
Inside the `hello` module, we've defined a `print_hello` function. This will | ||
also print out our hello world message. Modules allow you to split up your | ||
program into nice neat boxes of functionality, grouping common things together, | ||
and keeping different things apart. It's kinda like having a set of shelves: | ||
a place for everything and everything in its place. | ||
|
||
To call our `print_hello` function, we use the double colon (`::`): | ||
|
||
```{rust,ignore} | ||
hello::print_hello(); | ||
``` | ||
|
||
You've seen this before, with `io::stdin()` and `rand::random()`. Now you know | ||
how to make your own. However, crates and modules have rules about | ||
**visibility**, which controls who exactly may use the functions defined in a | ||
given module. By default, everything in a module is private, which means that | ||
it can only be used by other functions in the same module. This will not | ||
compile: | ||
|
||
```{rust,ignore} | ||
fn main() { | ||
hello::print_hello(); | ||
} | ||
|
||
mod hello { | ||
fn print_hello() { | ||
println!("Hello, world!"); | ||
} | ||
} | ||
``` | ||
|
||
It gives an error: | ||
|
||
```{notrust,ignore} | ||
Compiling modules v0.1.0 (file:/home/you/projects/modules) | ||
src/modules.rs:2:5: 2:23 error: function `print_hello` is private | ||
src/modules.rs:2 hello::print_hello(); | ||
^~~~~~~~~~~~~~~~~~ | ||
``` | ||
|
||
To make it public, we use the `pub` keyword: | ||
|
||
```{rust} | ||
fn main() { | ||
hello::print_hello(); | ||
} | ||
|
||
mod hello { | ||
pub fn print_hello() { | ||
println!("Hello, world!"); | ||
} | ||
} | ||
``` | ||
|
||
This will work: | ||
|
||
```{notrust,ignore} | ||
$ cargo build | ||
Compiling modules v0.1.0 (file:/home/you/projects/modules) | ||
$ | ||
``` | ||
|
||
Before we move on, let me show you one more Cargo command: `run`. `cargo run` | ||
is kind of like `cargo build`, but it also then runs the produced exectuable. | ||
Try it out: | ||
|
||
```{notrust,ignore} | ||
$ cargo run | ||
Compiling modules v0.1.0 (file:/home/steve/tmp/modules) | ||
Running `target/modules` | ||
Hello, world! | ||
$ | ||
``` | ||
|
||
Nice! | ||
|
||
There's a common pattern when you're building an executable: you build both an | ||
executable and a library, and put most of your logic in the library. That way, | ||
other programs can use that library to build their own functionality. | ||
|
||
Let's do that with our project. If you remember, libraries and executables | ||
are both crates, so while our project has one crate now, let's make a second: | ||
one for the library, and one for the executable. | ||
|
||
To make the second crate, open up `src/lib.rs` and put this code in it: | ||
|
||
```{rust} | ||
mod hello { | ||
pub fn print_hello() { | ||
println!("Hello, world!"); | ||
} | ||
} | ||
``` | ||
|
||
And change your `src/main.rs` to look like this: | ||
|
||
```{rust,ignore} | ||
extern crate modules; | ||
|
||
fn main() { | ||
modules::hello::print_hello(); | ||
} | ||
``` | ||
|
||
There's been a few changes. First, we moved our `hello` module into its own | ||
file, `src/lib.rs`. This is the file that Cargo expects a library crate to | ||
be named, by convention. | ||
|
||
Next, we added an `extern crate modules` to the top of our `src/main.rs`. This, | ||
as you can guess, lets Rust know that our crate relies on another, external | ||
crate. We also had to modify our call to `print_hello`: now that it's in | ||
another crate, we need to first specify the crate, then the module inside of it, | ||
then the function name. | ||
|
||
This doesn't _quite_ work yet. Try it: | ||
|
||
```{notrust,ignore} | ||
$ cargo build | ||
Compiling modules v0.1.0 (file:/home/you/projects/modules) | ||
/home/you/projects/modules/src/lib.rs:2:5: 4:6 warning: code is never used: `print_hello`, #[warn(dead_code)] on by default | ||
/home/you/projects/modules/src/lib.rs:2 pub fn print_hello() { | ||
/home/you/projects/modules/src/lib.rs:3 println!("Hello, world!"); | ||
/home/you/projects/modules/src/lib.rs:4 } | ||
/home/you/projects/modules/src/main.rs:4:5: 4:32 error: function `print_hello` is private | ||
/home/you/projects/modules/src/main.rs:4 modules::hello::print_hello(); | ||
^~~~~~~~~~~~~~~~~~~~~~~~~~~ | ||
error: aborting due to previous error | ||
Could not compile `modules`. | ||
``` | ||
|
||
First, we get a warning that some code is never used. Odd. Next, we get an error: | ||
`print_hello` is private, so we can't call it. Notice that the first error came | ||
from `src/lib.rs`, and the second came from `src/main.rs`: cargo is smart enough | ||
to build it all with one command. Also, after seeing the second error, the warning | ||
makes sense: we never actually call `hello_world`, because we're not allowed to! | ||
|
||
Just like modules, crates also have private visibility by default. Any modules | ||
inside of a crate can only be used by other modules in the crate, unless they | ||
use `pub`. In `src/lib.rs`, change this line: | ||
|
||
```{rust,ignore} | ||
mod hello { | ||
``` | ||
|
||
To this: | ||
|
||
```{rust,ignore} | ||
pub mod hello { | ||
``` | ||
|
||
And everything should work: | ||
|
||
```{notrust,ignore} | ||
$ cargo run | ||
Compiling modules v0.1.0 (file:/home/you/projects/modules) | ||
Running `target/modules` | ||
Hello, world! | ||
``` | ||
|
||
Let's do one more thing: add a `goodbye` module as well. Imagine a `src/lib.rs` | ||
that looks like this: | ||
|
||
```{rust,ignore} | ||
pub mod hello { | ||
pub fn print_hello() { | ||
println!("Hello, world!"); | ||
} | ||
} | ||
|
||
pub mod goodbye { | ||
pub fn print_goodbye() { | ||
println!("Goodbye for now!"); | ||
} | ||
} | ||
``` | ||
|
||
Now, these two modules are pretty small, but imagine we've written a real, large | ||
program: they could both be huge. So maybe we want to move them into their own | ||
files. We can do that pretty easily, and there are two different conventions | ||
for doing it. Let's give each a try. First, make `src/lib.rs` look like this: | ||
|
||
```{rust,ignore} | ||
pub mod hello; | ||
pub mod goodbye; | ||
``` | ||
|
||
This tells Rust that this crate has two public modules: `hello` and `goodbye`. | ||
|
||
Next, make a `src/hello.rs` that contains this: | ||
|
||
```{rust,ignore} | ||
pub fn print_hello() { | ||
println!("Hello, world!"); | ||
} | ||
``` | ||
|
||
When we include a module like this, we don't need to make the `mod` declaration, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. "the mod declaration" is at best ambiguous here. It could be interpreted as referring to the mod decl in lib.rs, which of course cannot be omitted |
||
it's just understood. This helps prevent 'rightward drift': when you end up | ||
indenting so many times that your code is hard to read. | ||
|
||
Finally, make a new directory, `src/goodbye`, and make a new file in it, | ||
`src/goodbye/mod.rs`: | ||
|
||
```{rust,ignore} | ||
pub fn print_goodbye() { | ||
println!("Bye for now!"); | ||
} | ||
``` | ||
|
||
Same deal, but we can make a folder with a `mod.rs` instead of `mod_name.rs` in | ||
the same directory. If you have a lot of modules, nested folders can make | ||
sense. For example, if the `goodbye` module had its _own_ modules inside of | ||
it, putting all of that in a folder helps keep our directory structure tidy. | ||
And in fact, if you place the modules in separate files, they're required to be | ||
in separate folders. | ||
|
||
This should all compile as usual: | ||
|
||
```{notrust,ignore} | ||
$ cargo build | ||
Compiling modules v0.1.0 (file:/home/you/projects/modules) | ||
$ | ||
``` | ||
|
||
We've seen how the `::` operator can be used to call into modules, but when | ||
we have deep nesting like `modules::hello::say_hello`, it can get tedious. | ||
That's why we have the `use` keyword. | ||
|
||
`use` allows us to bring certain names into another scope. For example, here's | ||
our main program: | ||
|
||
```{rust,ignore} | ||
extern crate modules; | ||
|
||
fn main() { | ||
modules::hello::print_hello(); | ||
} | ||
``` | ||
|
||
We could instead write this: | ||
|
||
```{rust,ignore} | ||
extern crate modules; | ||
|
||
use modules::hello::print_hello; | ||
|
||
fn main() { | ||
print_hello(); | ||
} | ||
``` | ||
|
||
By bringing `print_hello` into scope, we don't need to qualify it anymore. However, | ||
it's considered proper style to do write this code like like this: | ||
|
||
```{rust,ignore} | ||
extern crate modules; | ||
|
||
use modules::hello; | ||
|
||
fn main() { | ||
hello::print_hello(); | ||
} | ||
``` | ||
|
||
By just bringing the module into scope, we can keep one level of namespacing. | ||
|
||
# Testing | ||
|
||
attributes | ||
## Attributes | ||
|
||
stability markers | ||
## Stability Markers | ||
|
||
# Crates and Modules | ||
# Pointers | ||
|
||
visibility | ||
# Lambdas | ||
|
||
# iterators | ||
|
||
|
||
# Generics | ||
|
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
**crates**, and **module**s
doesn't seem to be rendered at least in github's previewThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've used it elsewhere, so it does work.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might be that it doesn't like star delimiters in the middle of a word.
Anyways, I'd suggest to embold the whole word. Bold makes the letters wider so having just a part emboldened seems just weird.