Ship ch 7 to nostarch

This commit is contained in:
Carol (Nichols || Goulding) 2016-11-05 14:01:23 -04:00
parent aaf1fc15b8
commit 46d88b4448

View File

@ -3,46 +3,49 @@
# Modules # Modules
When you write a program in Rust, your code might start off living solely in When you start writing programs in Rust, your code might live solely in the
the `main` function. As your code grows, you eventually move functionality out `main` function. As your code grows, youll eventually move functionality out
into functions, both for re-use and for nicer organization. By splitting your into other functions, both for re-use and for better organization. By splitting
code up into smaller chunks, each chunk is easier to understand on its own. So your code up into smaller chunks, each chunk is easier to understand on its
what happens when you start having too many functions? Rust has a module system own. But what happens if find yourself with too many functions? Rust has a
that tackles both the problem of wanting to be able to re-use code and the module system that handles the problem of wanting to to re-use code while
problem of keeping your code organized. keeping your code organized.
In the same way that you extract lines of code into a function, you can extract In the same way that you extract lines of code into a function, you can extract
functions (and other code like structs and enums too) into different modules. A functions (and other code like structs and enums too) into different modules. A
*module* is a namespace that contains definitions of functions or types, and *module* is a namespace that contains definitions of functions or types, and
those definitions can be visible outside their module or not. Here's an you can choose whether those definitions are visible outside their module
overview of how the bits fit together: (public) or not (private). Heres an overview of how modules work:
* `mod` declares a new module. * You declare a new module with the keyword `mod`
* Everything starts off as private, but the `pub` keyword makes it public. * By default, everything is set as private, but you can use the `pub` keyword
* The `use` keyword allows you to bring modules, or definitions inside of them, to make the module public, and therefore visible outside of the namespace.
into scope so that it's easier to refer to them. * The `use` keyword allows you to bring modules, or the definitions inside
modules, into scope so that its easier to refer to them.
We'll take a look at each of these parts and see how they fit into the whole. Well take a look at each of these parts and see how they fit into the whole.
## `mod` and the Filesystem ## `mod` and the Filesystem
Every module in Rust starts with the `mod` keyword. In this next example, we'll Well start our module example by making a new project with Cargo, but instead
start again by making a new project with Cargo. This time, instead of a binary, of creating a binary crate, were going to make a library crate: a project that
we're going to make a library: a project that other people would pull into their other people can pull into their projects as a dependency. We saw this with the
projects as a dependency. We saw this with the `rand` crate in Chapter 2. `rand` crate in Chapter 2.
Imagine that we're creating a library to provide some general networking Well create a skeleton of a library that provides some general networking
functionality, and we decide to call our library `communicator`. To create this functionality; were going to concentrate on the organization of the modules
library, we won't use the `--bin` option like we have before. This is because and functions, but not worry about what code goes in the function bodies. Well
by default cargo will create a library: call our library `communicator`. By default, cargo will create a library unless
another type of project is specified, so if we leave off the `--bin` option
that weve been using so far our project will be a library:
```bash ```bash
$ cargo new communicator $ cargo new communicator
$ cd communicator $ cd communicator
``` ```
Notice that Cargo generated `src/lib.rs` instead of `src/main.rs` for us, and Notice that Cargo generated *src/lib.rs* instead of *src/main.rs*. Inside
inside it we'll find this: *src/lib.rs* well find this:
Filename: src/lib.rs Filename: src/lib.rs
@ -55,14 +58,25 @@ mod tests {
} }
``` ```
This is an empty test to help us get our library started, instead of the binary Cargo creates an empty test to help us get our library started, rather
that says "Hello, world!" that we get with the `--bin` option. Let's ignore the than the “Hello, world!” binary that we get with the `--bin` option. Well look
`#[]` stuff and `mod tests` for a little bit, but we'll make sure to leave it at the `#[]` and `mod tests` syntax a little later, but for now just make sure
in `src/lib.rs` for later. to leave it in your *src/lib.rs*.
We're going to look at different ways we could choose to organize our library's Since we dont have a *src/main.rs*, theres nothing for Cargo to execute with
code, any of which could make sense depending on exactly what we were trying to the `cargo run` command. Therefore, we will be using the `cargo build` command
do. To start, add this code at the beginning of the file: to only compile our library crates code.
Were going to look at different options for organizing your librarys code
which will be suitable in a variety of situations, depending on the intentions
you have for your code.
### Module Definitions
For our `communicator` networking library, were first going to define a module
named `network` that contains the definition of a function called `connect`.
Every module definition in Rust starts with the `mod` keyword. Add this code to
the beginning of the *lib.rs* file, above the test code:
Filename: src/lib.rs Filename: src/lib.rs
@ -73,14 +87,16 @@ mod network {
} }
``` ```
This is our first module declaration. We use the `mod` keyword, followed by the After the `mod` keyword, we put the name of the module, `network`, then a block
name of the module, and then a block of code in curly braces. Everything inside of code in curly braces. Everything inside this block is inside the namespace
this block is inside the namespace `network`. In this case, we have a single `network`. In this case, we have a single function, `connect`. If we wanted to
function, `connect`. If we wanted to try and call this function from outside call this function from a script outside the `network` module, we would need to
the `network` module, we would say `network::connect()` rather than `connect()`. specify the module and use the namespace syntax `::`, like so:
`network::connect()`, rather than just `connect()`.
We could have multiple modules, side-by-side. For example, if we wanted a We can also have multiple modules, side-by-side, in the same *src/lib.rs* file.
`client` module: For example, to have a `client` module too, that also has a function named
`connect`, we can add:
Filename: src/lib.rs Filename: src/lib.rs
@ -96,10 +112,21 @@ mod client {
} }
``` ```
<caption>
Listing 7-1: The `network` module and the `client` module defined side-by-side
in *src/lib.rs*
</caption>
Now we have a `network::connect` function and a `client::connect` function. Now we have a `network::connect` function and a `client::connect` function.
These can have completely different functionality, and the function names do
not conflict with each other since theyre in different modules.
And we can put modules inside of modules. If we wanted to have `client` be We can also put modules inside of modules. This can be useful as your modules
within `network`: grow to keep related functionality organized together and separate
functionality apart. The choice of how you organize your code depends on how
you think about the relationship between the parts of your code. For instance,
the `client` code and its `connect` function might make more sense to users of
our library if it was inside the `network` namespace instead, like so:
Filename: src/lib.rs Filename: src/lib.rs
@ -115,11 +142,20 @@ mod network {
} }
``` ```
This gives us `network::connect` and `network::client::connect`. <caption>
Listing 7-2: Moving the `client` module inside of the `network` module
</caption>
In this way, modules form a tree. The contents of `src/lib.rs` are at the root In your *src/lib.rs* file, replace the existing `mod network` and `mod client`
of the project's tree, and the submodules form the leaves. Here's what our definitions with this one that has the `client` module as an inner module of
first example looks like when thought of this way: `network`. Now we have the functions `network::connect` and
`network::client::connect`: again, the two functions named `connect` dont
conflict with each other since theyre in different namespaces.
In this way, modules form a hierarchy. The contents of `src/lib.rs` are at the
topmost level, and the submodules are at lower levels. Heres what the
organization of our example from Listing 7-1 looks like when thought of this
way:
```text ```text
communicator communicator
@ -127,7 +163,7 @@ communicator
└── client └── client
``` ```
And here's the second: And heres the example from Listing 7-2:
```text ```text
communicator communicator
@ -135,13 +171,20 @@ communicator
└── client └── client
``` ```
More complicated projects can have a lot of modules. You can see that in Listing 7-2, `client` is a child of the `network` module,
rather than a sibling. More complicated projects can have a lot of modules, and
theyll need to be orgnaized logically in order to keep track of them. What
“logically” means in your project is up to you and depends on how you and users
of your library think about your projects domain. Use the techniques weve
shown here to create side-by-side modules and nested modules in whatever
structure you would like.
### Putting Modules in Another File ### Moving Modules to Other Files
Modules form a hierarchical, tree-like structure. So does another thing: Modules form a hierarchical structure, much like another structure in computing
file systems! The module system is the way that we split larger Rust projects up that youre used to: file systems! We can use Rusts module system along with
into multiple files. Let's imagine we have a module layout like this: multiple files to split Rust projects up so that not everything lives in
*src/lib.rs*. For this example, we will start with this code in *src/lib.rs*:
File: src/lib.rs File: src/lib.rs
@ -162,8 +205,26 @@ mod network {
} }
``` ```
Let's extract the `client` module into another file. First, we need to change <caption>
our code in `src/lib.rs`: Listing 7-3: Three modules, `client`, `network`, and `network::server` all
defined in *src/lib.rs*
</caption>
which has this module hierarchy:
```text
communicator
├── client
└── network
└── server
```
If these modules had many functions, and each function was getting long, we
would have to scroll through this file to find the code we wanted to work with.
This would be a good reason to pull each of the `client`, `network`, and
`server` modules out of *src/lib.rs* and into their own files. Lets start by
extracting the `client` module into another file. First, replace the `client`
module code in *src/lib.rs* with the following:
File: src/lib.rs File: src/lib.rs
@ -181,9 +242,17 @@ mod network {
} }
``` ```
We still say `mod client`, but instead of curly braces, we have a semicolon. <!-- I will add wingdings/ghosting in libreoffice /Carol -->
This lets Rust know that we have a module, but it's in another file with that
module's name. Open up `src/client.rs` and put this in it: Were still *defining* the `client` module here, but by removing the curly
braces and definitions inside the `client` module and replacing them with a
semicolon, were letting Rust know to look in another location for the code
defined inside that module.
So now we need to create the external file with that module name. Create a
`client.rs` file in your *src/* directory, then open it up and enter the
following, which is the `connect` function in the `client` module that we
removed in the previous step:
File: src/client.rs File: src/client.rs
@ -192,13 +261,19 @@ fn connect() {
} }
``` ```
Note that we don't need a `mod` declaration in this file. `mod` is for Note that we dont need a `mod` declaration in this file; thats because we
declaring a new module, and we've already declared this module in `src/lib.rs`. already declared the `client` module with `mod` in `src/lib.rs`. This file just
This file provides the _contents_ of the `client` module. If we put a `mod provides the *contents* of the `client` module. If we put a `mod client` here,
client` here, we'd be giving the `client` module its own submodule named wed be giving the `client` module its own submodule named `client`!
`client`!
Now, everything should compile successfully, but with a few warnings: Rust only knows to look in *src/lib.rs* by default. If we want to add more
files to our project, we need to tell Rust in *src/lib.rs* to look in other
files; this is why `mod client` needs to be defined in *src/lib.rs* and cant
be defined in *src/client.rs*.
Now, everything should compile successfully, though youll get a few warnings.
Remember to use `cargo build` instead of `cargo run` since we have a library
crate rather than a binary crate:
```bash ```bash
$ cargo build $ cargo build
@ -223,11 +298,13 @@ warning: function is never used: `connect`, #[warn(dead_code)] on by default
| ^ | ^
``` ```
Don't worry about those warnings for now; we'll clear them up in a future These warnings tell us that we have functions that are never used. Dont worry
section. They're just warnings, we've built things successfully! about those warnings for now; well address them later in the chapter. The good
news is that theyre just warnings; our project was built successfully!
Let's extract the `network` module into its own file next, using the same Lets extract the `network` module into its own file next, using the same
pattern. Change `src/lib.rs` to look like this: pattern. In `src/lib.rs`, delete the body of the `network` module and add a
semicolon to the declaration, like so:
Filename: src/lib.rs Filename: src/lib.rs
@ -237,7 +314,7 @@ mod client;
mod network; mod network;
``` ```
And then put this in `src/network.rs` Then create a new `src/network.rs` file and enter the following:
Filename: src/network.rs Filename: src/network.rs
@ -251,10 +328,15 @@ mod server {
} }
``` ```
And then run `cargo build` again. Success! We have one more module to extract: Notice that we still have a `mod` declaration within this module file;
`server`. Unfortunately, our current tactic of extracting a module into a file this is because we still want `server` to be a sub-module of `network`.
named after that module won't work. Let's try it anyway. Modify
`src/network.rs` to look like this: Now run `cargo build` again. Success! We have one more module to extract:
`server`. Because its a sub-module—that is, a module within a module—our
current tactic of extracting a module into a file named after that module wont
work. Were going to try anyway so that we can see the error. First change
*src/network.rs* to have `mod server;` instead of the `server` modules
contents:
Filename: src/network.rs Filename: src/network.rs
@ -265,7 +347,8 @@ fn connect() {
mod server; mod server;
``` ```
Put this in `src/server.rs` Then create a `src/server.rs` file and enter the contents of the `server`
module that we extracted:
Filename: src/server.rs Filename: src/server.rs
@ -274,7 +357,7 @@ fn connect() {
} }
``` ```
When we try to `cargo build`, we'll get an error: When we try to `cargo build`, well get this error:
```bash ```bash
$ cargo build $ cargo build
@ -297,35 +380,40 @@ note: ... or maybe `use` the module `server` instead of possibly redeclaring it
| ^^^^^^ | ^^^^^^
``` ```
This error is actually pretty helpful. It points out something we didn't know <caption>
that we could do yet: Listing 7-4: Error when trying to extract the `server` submodule into
*src/server.rs*
</caption>
The error says we `cannot declare a new module at this location` and is
pointing to the `mod server;` line in `src/network.rs`. So `src/network.rs` is
different than `src/lib.rs` somehow; lets keep reading to understand why.
The note in the middle of Listing 7-4 is actually pretty helpful, as it points
out something we havent yet talked about doing:
> note: maybe move this module `network` to its own directory via > note: maybe move this module `network` to its own directory via
`network/mod.rs` `network/mod.rs`
Here's the problem: in our case, we have different names for our modules:
`client` and `network::server`. But what if we had `client` and
`network::client`, or `server` and `network::server`? Having two modules at
different places in the module hierarchy have the same name is completely
valid, but then which module would the files `src/client.rs` and
`src/server.rs`, respectively, be for?
Instead of continuing to follow the same file naming pattern we used Instead of continuing to follow the same file naming pattern we used
previously, we can do what the error suggests. We'll make a new _directory_, previously, we can do what the note suggests:
move `src/server.rs` into it, and change `src/network.rs` to
`src/network/mod.rs`. Then, when we try to build: 1. Make a new *directory* named *network*, the parent modules name
2. Move the *src/network.rs* file into the new *network* directory and rename
it so that it is now *src/network/mod.rs*
3. Move the submodule file *src/server.rs* into the *network* directory
Here are commands to carry out these steps:
```bash ```bash
$ mkdir src/network $ mkdir src/network
$ mv src/server.rs src/network
$ mv src/network.rs src/network/mod.rs $ mv src/network.rs src/network/mod.rs
$ cargo build $ mv src/server.rs src/network
Compiling communicator v0.1.0 (file:///projects/communicator)
<warnings>
$
``` ```
It works! So now our module layout looks like this: Now if we try to `cargo build`, compilation will work (well still have
warnings though). Our module layout still looks like this, which is exactly the
same as it did when we had all the code in *src/lib.rs* in Listing 7-3:
```text ```text
communicator communicator
@ -334,7 +422,7 @@ communicator
└── server └── server
``` ```
And the corresponding file layout looks like this: The corresponding file layout now looks like this:
```text ```text
├── src ├── src
@ -345,10 +433,47 @@ And the corresponding file layout looks like this:
│   └── server.rs │   └── server.rs
``` ```
So when we wanted to extract the `network::server` module, why did we have to
also change the *src/network.rs* file into the *src/network/mod.rs* file, and
also put the code for `network::server` in the `network` directory in
*src/network/server.rs*, instead of just being able to extract the
*network::server* into *src/server.rs*? The reason is that Rust wouldnt be
able to tell that `server` was supposed to be a submodule of `network` if the
*server.rs* file was in the *src* directory. To make it clearer why Rust cant
tell, lets consider a different example where we have this module hierarchy
with all the definitions in *src/lib.rs*:
```text
communicator
├── client
└── network
└── client
```
In this example, we have three modules again, `client`, `network`, and
`network::client`. If we follow the same steps we originally did above for
extracting modules into files, for the `client` module we would create
*src/client.rs*. For the `network` module, we would create *src/network.rs*.
Then we wouldnt be able to extract the `network::client` module into a
*src/client.rs* file, because that already exists for the top-level `client`
module! If we put the code in both the `client` and `network::client` modules
in the *src/client.rs* file, Rust would not have any way to know whether the
code was for `client` or for `network::client`.
Therefore, once we wanted to extract a file for the `network::client` submodule
of the `network` module, we needed to create a directory for the `network`
module instead of a *src/network.rs* file. The code that is in the `network`
module then goes into the *src/network/mod.rs* file, and the submodule
`network::client` can have its own *src/network/client.rs* file. Now the
top-level *src/client.rs* is unambiguously the code that belongs to the
`client` module.
### Rules of Module File Systems
In summary, these are the rules of modules with regards to files: In summary, these are the rules of modules with regards to files:
* If a module named `foo` has no submodules, you should put the declarations in * If a module named `foo` has no submodules, you should put the declarations
the `foo` module in a file named `foo.rs`. for `foo` in a file named `foo.rs`.
* If a module named `foo` does have submodules, you should put the declarations * If a module named `foo` does have submodules, you should put the declarations
for `foo` in a file named `foo/mod.rs`. for `foo` in a file named `foo/mod.rs`.
* The first two rules apply recursively, so that if a module named `foo` has a * The first two rules apply recursively, so that if a module named `foo` has a
@ -361,19 +486,23 @@ In summary, these are the rules of modules with regards to files:
│   └── mod.rs (contains the declarations in `foo`, including `mod bar`) │   └── mod.rs (contains the declarations in `foo`, including `mod bar`)
``` ```
* The modules themselves should be declared in their parent module's file using * The modules themselves should be declared in their parent modules file using
the `mod` keyword. the `mod` keyword.
Next, we'll talk about the `pub` keyword, and get rid of those warnings! Next, well talk about the `pub` keyword, and get rid of those warnings!
## Controlling Visibility with `pub` ## Controlling Visibility with `pub`
At the end of the last section, we had a project, `communicator`, and when we compiled it, we got some strange warnings: We resolved the error messages shown in Listing 7-4 by moving the `network` and
`network::server` code into the *src/network/mod.rs* and
*src/network/server.rs* files, respectively. At that point, `cargo build` was
able to build our project, but we still get some warning messages about the
`client::connect`, `network::connect`, and `network::server::connect` functions
not being used:
```bash ```bash
Compiling communicator v0.1.0 (file:///projects/communicator)
warning: function is never used: `connect`, #[warn(dead_code)] on by default warning: function is never used: `connect`, #[warn(dead_code)] on by default
--> src/client.rs:1:1 src/client.rs:1:1
| |
1 | fn connect() { 1 | fn connect() {
| ^ | ^
@ -391,13 +520,16 @@ warning: function is never used: `connect`, #[warn(dead_code)] on by default
| ^ | ^
``` ```
Why does this happen? After all, we're building a library. What if these three So why are we receiving these warnings? After all, were building a library
functions are the public interface that we want our *users* to use? We won't with functions that are intended to be used by our *users*, and not necessarily
necessarily be using them within our own project, but the point of creating them by us within our own project, so it shouldnt matter that these `connect`
is that they *will* be used by another project. Let's try using them as if we functions go unused. The point of creating them is that they will be used by
were another project using our library to see what happens and understand why another project and not our own.
we're getting these unused function warnings. Create a `src/main.rs` file with
this code: To understand why this program invokes these warnings, lets try using the
`connect` library as if we were another project, calling it externally. We can
do that by creating a binary crate in the same directory as our library crate,
by making a `src/main.rs` file containing this code:
Filename: src/main.rs Filename: src/main.rs
@ -409,22 +541,19 @@ fn main() {
} }
``` ```
We need the `extern crate` line to bring our `communicator` library crate into We use the `extern crate` command to bring the `communicator` library crate
scope, because our package actually now contains *two* crates. Cargo treats into scope, because our package actually now contains *two* crates. Cargo
src/main.rs as the crate root of a binary crate, and we also have our existing treats *src/main.rs* as the root file of a binary crate, which is separate from
library crate. This pattern is quite common for executable crates: most the existing library crate whose root file is *src/lib.rs*. This pattern is
functionality is in a library crate, and the executable crate uses that quite common for executable projects: most functionality is in a library crate,
library. This way, other programs can also use the library crate, and its a and the binary crate uses that library crate. This way, other programs can also
nice separation of concerns. use the library crate, and its a nice separation of concerns.
Our binary crate right now just calls our library's `connect` function from Our binary crate right now just calls our librarys `connect` function from the
the `client` module; we picked that one since it's the first warning in our `client` module. However, invoking `cargo build` will now give us an error
build output above. Invoking `cargo build` will now give us an error after the after the warnings:
warnings:
```bash ```bash
$ cargo build
Compiling communicator v0.1.0 (file:///projects/communicator)
error: module `client` is private error: module `client` is private
--> src/main.rs:4:5 --> src/main.rs:4:5
| |
@ -432,19 +561,30 @@ error: module `client` is private
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``` ```
Ah ha! The `client` module is private. This is the first time we've run into Ah ha! This tells us that the `client` module is private, and this is the crux
the concepts of 'public' and 'private' in the context of Rust. There's no of the warnings. Its also the first time weve run into the concepts of
keyword to make something private; that's the default state. In this default *public* and *private* in the context of Rust. The default state of all code in
state, no one else could possibly use it, so if we don't use it within our Rust is private: no one else is allowed to use the code. If you dont use a
library crate, Rust will warn us that it's unused. Once we tell Rust something private function within your own program, since your own program is the only
is public, Rust knows that we intend for code external to our crate to use it, code allowed to use that function, Rust will warn you that the function has
and Rust considers theoretical external usage that is now possible to count as gone unused.
being used. Thus, when something is marked as public, Rust will stop warning us
that it is unused.
To tell Rust we want to make something public, we add the `pub` keyword. This Once we specify that a function like `client::connect` is public, not only will
keyword goes before the declaration of the item we want to make public. Let's our call to that function from our binary crate be allowed, the warning that
modify `src/lib.rs` to make the `client` module public to fix the error we got: the function is unused will go away. Marking something public lets Rust know
that we intend for the function to be used by code outside of our program. Rust
considers the theoretical external usage thats now possible as the function
“being used.” Thus, when something is marked as public, Rust will not require
that its used in our own program and will stop warning that the item is
unused.
### Making a Function Public
To tell Rust to make something public, we add the `pub` keyword to the start of
the declaration of the item we want to make public. Well focus on fixing the
warning that tells us that `client::connect` has gone unused for now, as well
as the “module `client` is private” error from our binary crate. Modify
`src/lib.rs` to make the `client` module public, like so:
Filename: src/lib.rs Filename: src/lib.rs
@ -454,11 +594,9 @@ pub mod client;
mod network; mod network;
``` ```
The `pub` goes right before `mod`. Let's try building again: The `pub` goes right before `mod`. Lets try building again:
```bash ```bash
$ cargo build
Compiling communicator v0.1.0 (file:///projects/communicator)
<warnings> <warnings>
error: function `connect` is private error: function `connect` is private
--> src/main.rs:4:5 --> src/main.rs:4:5
@ -468,8 +606,8 @@ error: function `connect` is private
``` ```
Hooray! We have a different error! Yes, different error messages are a cause Hooray! We have a different error! Yes, different error messages are a cause
for celebration. The new error says "function `connect` is private", so let's for celebration. The new error says “function `connect` is private”, so lets
edit `src/client.rs` to make `client::connect` public: edit `src/client.rs` to make `client::connect` public too:
Filename: src/client.rs Filename: src/client.rs
@ -481,8 +619,6 @@ pub fn connect() {
And run `cargo build` again: And run `cargo build` again:
```bash ```bash
cargo build
Compiling communicator v0.1.0 (file:///projects/communicator)
warning: function is never used: `connect`, #[warn(dead_code)] on by default warning: function is never used: `connect`, #[warn(dead_code)] on by default
--> src/network/mod.rs:1:1 --> src/network/mod.rs:1:1
| |
@ -496,18 +632,18 @@ warning: function is never used: `connect`, #[warn(dead_code)] on by default
| ^ | ^
``` ```
It compiled! And the warning about `client::connect` not being used is gone! It compiled, and the warning about `client::connect` not being used is gone!
Making functions public isn't the only way to fix unused code warnings: if Unused code warnings dont always indicate that something needs to be made
we *didn't* want these functions to be part of our public API and we got these public: if you *didnt* want these functions to be part of your public API,
warnings, the warnings could be alerting us to code we no longer needed and unused code warnings could be alerting you to code you no longer needed and can
could safely delete. They could also be alerting us to a bug, if we safely delete. They could also be alerting you to a bug, if you had just
had just accidentally removed all places within our library where we called accidentally removed all places within your library where this function is
this function. called.
However, we *do* want the other two functions to be part of our crate's public In our case though, we *do* want the other two functions to be part of our
API, so let's mark them as `pub` as well to get rid of the remaining warnings. crates public API, so lets mark them as `pub` as well to try to get rid of
Modify `src/network/mod.rs` to be: the remaining warnings. Modify `src/network/mod.rs` to be:
Filename: src/network/mod.rs Filename: src/network/mod.rs
@ -521,8 +657,6 @@ mod server;
And compile: And compile:
```bash ```bash
$ cargo build
Compiling communicator v0.1.0 (file:///projects/communicator)
warning: function is never used: `connect`, #[warn(dead_code)] on by default warning: function is never used: `connect`, #[warn(dead_code)] on by default
--> src/network/mod.rs:1:1 --> src/network/mod.rs:1:1
| |
@ -536,11 +670,12 @@ warning: function is never used: `connect`, #[warn(dead_code)] on by default
| ^ | ^
``` ```
Hmmm, it says this is still dead, even though it's `pub`. While the function is Hmmm, were still getting an unused function warning even though
public within the module, the `network` module it's in is not public. We're `network::connect` is set to `pub`. This is because the function is public
working from the interior of the library out this time, as opposed to with within the module, but the `network` module that the function resides in is not
`client` where we worked from the outside in. Let's change `src/lib.rs` to add public. Were working from the interior of the library out this time, where
the same fix though, by making `network` public like `client` is: with `client::connect` we worked from the outside in. We need to change
`src/lib.rs` to make `network` public too:
Filename: src/lib.rs Filename: src/lib.rs
@ -553,8 +688,6 @@ pub mod network;
Now if we compile, that warning is gone: Now if we compile, that warning is gone:
```bash ```bash
$ cargo build
Compiling communicator v0.1.0 (file:///projects/communicator)
warning: function is never used: `connect`, #[warn(dead_code)] on by default warning: function is never used: `connect`, #[warn(dead_code)] on by default
--> src/network/server.rs:1:1 --> src/network/server.rs:1:1
| |
@ -562,19 +695,21 @@ warning: function is never used: `connect`, #[warn(dead_code)] on by default
| ^ | ^
``` ```
Only one last warning! Try to fix this one on your own! Only one warning left! Try to fix this one on your own!
### Privacy Rules ### Privacy Rules
Overall, these are the rules for item visibility: Overall, these are the rules for item visibility:
1. If an item is public, then it can be accessed through any of its 1. If an item is public, it can be accessed through any of its
parent modules. parent modules.
2. If an item is private, it may be accessed by the current module and its 2. If an item is private, it may be accessed only by the current module and its
child modules. child modules.
Let's look at a few more examples to get some practice. What if we had this ### Privacy Examples
code in a new project's `src/lib.rs`:
Lets look at a few more examples to get some practice. Create a new libary
project and enter the code in Listing 7-5 into your new projects `src/lib.rs`:
Filename: src/lib.rs Filename: src/lib.rs
@ -599,49 +734,55 @@ fn try_me() {
} }
``` ```
Before you try to compile this code, make a guess about which lines in Before you try to compile this code, make a guess about which lines in `try_me`
`try_me` will have errors. function will have errors. Then try compiling to see if you were right, and read
on for discussion of the errors!
Ready? Let's talk through them! #### Looking at the Errors
The `try_me` function is in the root module of our project. The module named The `try_me` function is in the root module of our project. The module named
`outermost` is private, but the second rule says we're allowed to access it `outermost` is private, but the second privacy rule says the `try_me` function
since `outermost` is in our current, root module. is allowed to access the `outermost` module since `outermost` is in the current
(root) module, as is `try_me`.
The function call `outermost::middle_function()` will work. `middle_function` The call to `outermost::middle_function` will work. This is because
is public, and we are accessing it through its parent module, `outermost`, `middle_function` is public, and `try_me` is accessing `middle_function`
which we just determined we can access in the previous paragraph. through its parent module, `outermost` We determined in the previous paragraph
that this module is accessible.
`outermost::middle_secret_function()` will cause a compilation error. The call to `outermost::middle_secret_function` will cause a compilation error.
`middle_secret_function` is private, so the second rule applies. Our current `middle_secret_function` is private, so the second rule applies. The root
root module is neither the current module of `middle_secret_function` module is neither the current module of `middle_secret_function` (`outermost`
(`outermost` is), nor is it a child module of the current module of is), nor is it a child module of the current module of `middle_secret_function`.
`middle_secret_function`.
The module named `inside` is private and has no child modules, so it can only The module named `inside` is private and has no child modules, so it can only
be accessed by its current module, `outermost`. That means the `try_me` be accessed by its current module, `outermost`. That means the `try_me`
function is not allowed to call `outermost::inside::inner_function()` or function is not allowed to call `outermost::inside::inner_function` or
`outermost::inside::secret_function()`. `outermost::inside::secret_function` either.
Here are some changes to try making with this code. Try each one, make a guess #### Fixing the Errors
about what will be allowed or not, compile to see if you're right, and use the
rules to understand why. Here are some suggestions for changing the code in an attempt to fix the
errors. Before you try each one, make a guess as to whether it will fix the
errors, then compile to see if youre right and use the privacy rules to
understand why.
* What if the `inside` module was public? * What if the `inside` module was public?
* What if `outside` was public and `inside` was private? * What if `outside` was public and `inside` was private?
* What if, in the body of `inner_function`, we called * What if, in the body of `inner_function`, you called
`::outermost::middle_secret_function()`? (The two colons at the beginning `::outermost::middle_secret_function()`? (The two colons at the beginning
mean that we want to refer to the namespaces starting from the root mean that we want to refer to the namespaces starting from the root
namespace.) namespace.)
Feel free to design more experiments and try them out! Feel free to design more experiments and try them out!
Next, let's talk about bringing items into a scope with the `use` keyword. Next, lets talk about bringing items into a scope with the `use` keyword.
## Importing Names with `use` ## Importing Names
We've seen how we can call functions defined within a module by using the Weve covered how to call functions defined within a module using the module
module name as part of the call, like this: name as part of the call, as in the call to the `namespaces` function shown
here in Listing 7-6.
Filename: src/main.rs Filename: src/main.rs
@ -659,9 +800,19 @@ fn main() {
} }
``` ```
However, referring to the fully qualified name can get quite lengthy, as we see <caption>
in that example. To solve this issue, Rust has a keyword, `use`. It works like Listing 7-6: Calling a function by fully specifying its enclosing modules
this: namespaces
</caption>
As you can see, referring to the fully qualified name can get quite lengthy.
Luckily, Rust has a keyword to make these calls more concise.
### Concise Imports with `use`
Rusts `use` keyword works to shorten lengthy function calls by bringing the
modules of the function you want to call into a scope. Heres an example of
bringing the `a::series::of` namespace into a binary crates root scope:
Filename: src/main.rs Filename: src/main.rs
@ -681,10 +832,16 @@ fn main() {
} }
``` ```
We can `use` a module, and that will bring its name into scope. This allows us The line `use a::series::of;` has made it so that anywhere in this scope that
to shorten our function call, only requiring us to type the final module name, we would want to refer to the `of` namespace, instead of having to say
not the entire chain of them. `use` is quite powerful and can bring all kinds `a::series::of`, we can replace that with `of`.
of things into scope. For example, we could `use` the function itself:
The `use` keyword brings only what we have specified into scope; it does not
bring children of modules into scope. Thats why we still have to say
`of::namespaces` when we want to call the `namespaces` function.
We could have chosen to bring the function itself into scope, by instead
specifying the function in the `use` as follows:
```rust ```rust
pub mod a { pub mod a {
@ -702,10 +859,13 @@ fn main() {
} }
``` ```
Enums also form this kind of namespace; we can import an enum's variants with This allows us to exclude any of the modules and just reference the function at
`use` as well. For any kind of `use` statement, if you are importing multiple the callsite.
items from one namespace, you can list them using curly braces and commas in
the last position, like so: Since enums also form this kind of namespace, we can import an enums variants
with `use` as well. For any kind of `use` statement, if youre importing
multiple items from one namespace, you can list them using curly braces and
commas in the last position, like so:
```rust ```rust
enum TrafficLight { enum TrafficLight {
@ -719,13 +879,14 @@ use TrafficLight::{Red, Yellow};
fn main() { fn main() {
let red = Red; let red = Red;
let yellow = Yellow; let yellow = Yellow;
let green = TrafficLight::Green; // because we didn't use TrafficLight::Green let green = TrafficLight::Green; // because we didnt `use` TrafficLight::Green
} }
``` ```
### Glob Imports with `*` ### Glob Imports with `*`
If you'd like to import all the items in a namespace at once, you can use `*`: To import all the items in a namespace at once, we can use the `*` syntax. For
example:
```rust ```rust
enum TrafficLight { enum TrafficLight {
@ -743,14 +904,15 @@ fn main() {
} }
``` ```
The `*` is called a 'glob', and it will import everything that's visible inside The `*` is called a *glob*, and it will import everything thats visible inside
of the namespace. Globs should be used sparingly: they are convenient, but you of the namespace. Globs should be used sparingly: they are convenient, but you
might also pull in more things than you expected and cause naming conflicts. might also pull in more things than you expected and cause naming conflicts.
### Using `super` to Access a Parent Module ### Using `super` to Access a Parent Module
Remember when we created our crate that Cargo made a `tests` module for us? As you now know, when you create a library crate, Cargo makes a `tests` module
Let's talk about that now. It was in `src/lib.rs`: for you. Lets go into more detail about that now. In your `communicator`
project, open `src/lib.rs`.
Filename: src/lib.rs Filename: src/lib.rs
@ -767,14 +929,24 @@ mod tests {
} }
``` ```
We'll explain more about testing in Chapter XX, but parts of this should make Well explain more about testing in Chapter 12, but parts of this should make
sense now: we have a module named `tests` that lives next to our other modules sense now: we have a module named `tests` that lives next to our other modules
and contains one function named `it_works`. Even though there are special and contains one function named `it_works`. Even though there are special
annotations, the `tests` module is just another module! annotations, the `tests` module is just another module! So our module hierarchy
looks like this:
Since tests are for exercising the code within our library, let's try to call ```text
communicator
├── client
├── network
| └── client
└── tests
```
Tests are for exercising the code within our library, so lets try to call
our `client::connect` function from this `it_works` function, even though our `client::connect` function from this `it_works` function, even though
we're not going to be checking any functionality right now: were not going to be checking any functionality right now:
Filename: src/lib.rs Filename: src/lib.rs
@ -806,35 +978,41 @@ warning: function is never used: `connect`, #[warn(dead_code)] on by default
| ^ | ^
``` ```
Why doesn't this compile? It's not because we don't have `communicator::` in The compilation failed, but why? We dont need to place `communicator::` in
front of the function like we had in `src/main.rs`: we are definitely within front of the function like we did in `src/main.rs` because we are definitely
the `communicator` library crate here. The reason is that paths anywhere except within the `communicator` library crate here. The reason is that paths are
in a `use` statement are relative to the current module (In a `use` statement, always relative to the current module, which here is `tests`. The only
they're relative to the crate root by default). Our `tests` module doesn't have exception is in a `use` statement, where paths are relative to the crate root
a `client` module in its scope! by default. Our `tests` module needs the `client` module in its scope!
So how do we get back up one module? We can either use leading colons to say So how do we get back up one module in the module hierarchy to be able to call
that we want to start from the root and list the whole path: the `client::connect` function in the `tests` module? In the `tests` module, we
can either use leading colons to let Rust know that we want to start from the
root and list the whole path:
```rust,ignore ```rust,ignore
::client::connect(); ::client::connect();
``` ```
Or we can use `super` to move up one module in the hierarchy: Or we can use `super` to move up one module in the hierarchy from our current
module:
```rust,ignore ```rust,ignore
super::client::connect(); super::client::connect();
``` ```
If we were deep in the module hierarchy, starting from the root every time These two options dont look all that different in this example, but if youre
would get long. Plus, if we rearrange our modules by moving a subtree to deeper in a module hierarchy, starting from the root every time would get long.
another place, there might be a lot of places the path would need to be updated In those cases, using `super` to get from the current module to sibling modules
if we always used the path from the root. is a good shortcut. Plus, if youve specified the path from the root in many
places in your code and then you rearrange your modules by moving a subtree to
another place, youd end up needing to update the path in a lot of places,
which would be tedious.
It would also be annoying to have to type `super::` all the time in each test, It would also be annoying to have to type `super::` all the time in each test,
but we now have a tool for that solution: `use`! `super::` is special and but youve already seen the tool for that solution: `use`! The `super::`
changes the path we give to `use` so that it is relative to the parent module functionality changes the path you give to `use` so that it is relative to the
instead of to the root module. parent module instead of to the root module.
For these reasons, in the `tests` module especially, `use super::something` is For these reasons, in the `tests` module especially, `use super::something` is
usually the way to go. So now our test looks like this: usually the way to go. So now our test looks like this:
@ -867,6 +1045,11 @@ test tests::it_works ... ok
test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured test result: ok. 1 passed; 0 failed; 0 ignored; 0 measured
``` ```
## Summary
Now you know techniques for organizing your code! Use these to group related Now you know techniques for organizing your code! Use these to group related
functionality together, keep files from getting too long, and present a tidy functionality together, keep files from getting too long, and present a tidy
public API to users of your library. public API to users of your library.
Next, lets look at some collection data structures in the standard library
that you can make use of in your nice, neat code!