mirror of
https://github.com/rust-lang-cn/book-cn.git
synced 2025-01-24 08:00:24 +08:00
Ship ch 7 to nostarch
This commit is contained in:
parent
aaf1fc15b8
commit
46d88b4448
@ -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, you’ll 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). Here’s 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 it’s easier to refer to them.
|
||||||
|
|
||||||
We'll take a look at each of these parts and see how they fit into the whole.
|
We’ll 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
|
We’ll 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, we’re 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
|
We’ll create a skeleton of a library that provides some general networking
|
||||||
functionality, and we decide to call our library `communicator`. To create this
|
functionality; we’re 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. We’ll
|
||||||
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 we’ve 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* we’ll 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. We’ll 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 don’t have a *src/main.rs*, there’s 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 crate’s code.
|
||||||
|
|
||||||
|
We’re going to look at different options for organizing your library’s 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, we’re 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 they’re 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` don’t
|
||||||
|
conflict with each other since they’re 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. Here’s 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 here’s 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
|
||||||
|
they’ll 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 project’s domain. Use the techniques we’ve
|
||||||
|
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 you’re used to: file systems! We can use Rust’s 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. Let’s 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:
|
We’re still *defining* the `client` module here, but by removing the curly
|
||||||
|
braces and definitions inside the `client` module and replacing them with a
|
||||||
|
semicolon, we’re 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 don’t need a `mod` declaration in this file; that’s 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
|
we’d 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 can’t
|
||||||
|
be defined in *src/client.rs*.
|
||||||
|
|
||||||
|
Now, everything should compile successfully, though you’ll 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. Don’t worry
|
||||||
section. They're just warnings, we've built things successfully!
|
about those warnings for now; we’ll address them later in the chapter. The good
|
||||||
|
news is that they’re just warnings; our project was built successfully!
|
||||||
|
|
||||||
Let's extract the `network` module into its own file next, using the same
|
Let’s 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 it’s a sub-module—that is, a module within a module—our
|
||||||
|
current tactic of extracting a module into a file named after that module won’t
|
||||||
|
work. We’re going to try anyway so that we can see the error. First change
|
||||||
|
*src/network.rs* to have `mod server;` instead of the `server` module’s
|
||||||
|
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`, we’ll 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; let’s keep reading to understand why.
|
||||||
|
|
||||||
|
The note in the middle of Listing 7-4 is actually pretty helpful, as it points
|
||||||
|
out something we haven’t 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 module’s 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 (we’ll 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 wouldn’t 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 can’t
|
||||||
|
tell, let’s 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 wouldn’t 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 module’s file using
|
||||||
the `mod` keyword.
|
the `mod` keyword.
|
||||||
|
|
||||||
Next, we'll talk about the `pub` keyword, and get rid of those warnings!
|
Next, we’ll 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, we’re 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 shouldn’t 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, let’s 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 it’s a
|
and the binary crate uses that library crate. This way, other programs can also
|
||||||
nice separation of concerns.
|
use the library crate, and it’s 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 library’s `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. It’s also the first time we’ve 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 don’t 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 that’s now possible as the function
|
||||||
|
“being used.” Thus, when something is marked as public, Rust will not require
|
||||||
|
that it’s 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. We’ll 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`. Let’s 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 let’s
|
||||||
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 don’t 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 *didn’t* 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.
|
crate’s public API, so let’s 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, we’re 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. We’re 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`:
|
|
||||||
|
Let’s 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 project’s `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 you’re 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, let’s 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
|
We’ve 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 module’s
|
||||||
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`
|
||||||
|
|
||||||
|
Rust’s `use` keyword works to shorten lengthy function calls by bringing the
|
||||||
|
modules of the function you want to call into a scope. Here’s an example of
|
||||||
|
bringing the `a::series::of` namespace into a binary crate’s 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. That’s 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 enum’s variants
|
||||||
|
with `use` as well. For any kind of `use` statement, if you’re 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 didn’t `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 that’s 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. Let’s 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
|
We’ll 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 let’s 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:
|
we’re 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 don’t 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 don’t look all that different in this example, but if you’re
|
||||||
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 you’ve 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, you’d 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 you’ve 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, let’s look at some collection data structures in the standard library
|
||||||
|
that you can make use of in your nice, neat code!
|
||||||
|
Loading…
Reference in New Issue
Block a user