Start crates and modules

pull/48/head
Dhghomon 4 years ago committed by GitHub
parent 961e71f081
commit 0fd4fdde56
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -90,6 +90,7 @@ It is now late July, and *Easy Rust* is about 200 pages long. I am still writing
- [Box around traits](#box-around-traits)
- [Default and the builder pattern](#default-and-the-builder-pattern)
- [Deref and DerefMut](#deref-and-derefmut)
- [Crates and modules](#crates-and-modules)
## Rust Playground
@ -8945,3 +8946,190 @@ fn main() {
This just prints `[5, 5]`. Our code is now very strange for someone to read. We can read `Deref` just above `.main()` and figure out that `*billy` means `i8`, but what if there was a lot of code? Maybe our code is 2000 lines long, and suddenly we have to figure out why we are `.push()`ing `*billy`. `Character` is certainly more than just a smart pointer for `i8`.
Of course, it is not illegal to write `hit_points_vec.push(*billy)`, but it makes the code look very strange. Probably a simple `.get_hp()` method would be much better, or another struct that holds the characters. Then you could iterate through and push the `hit_points` for each one. `Deref` gives a lot of power but it's good to make sure that the code is logical.
## Crates and modules
Every time you write code in Rust, you are writing it in a `crate`. A `crate` is the file, or files, that go together for your code. Inside the file you write you can also make a `mod`. A `mod` is a space for functions, structs, etc. and is used for a few reasons:
- Building your code: it helps you think about the general structure of your code. This can be important as your code gets larger and larger.
- Reading your code: people can understand your code more easily. For example, when you see `std::collections::HashMap` you know that it's in std inside the module `collections`. This gives you a hint that maybe there are more collection types inside `collections` that you can try.
- Privacy: everything starts out as private. That lets you keep users from using functions directly.
To make a `mod`, just write `mod` and start a code block with `{}`. We will make a mod called `print_things` that has some printing-related functions.
```rust
mod print_things {
use std::fmt::Display;
fn prints_one_thing<T: Display>(input: T) { // Print anything that implements Display
println!("{}", input)
}
}
fn main() {}
```
You can see that we wrote `use std::fmt::Display;` inside `print_things`, because it is a separate space. If you wrote `use std::fmt::Display;` inside `.main()` it wouldn't help. Also, we can't call it from `.main()` right now. Without the `pub` keyword in front of `fn` it will stay private. Let's try to call it without `pub`. Here's one way to write it:
```rust
// 🚧
fn main() {
crate::print_things::prints_one_thing(6);
}
```
`crate` means inside this file. Inside that is the mod `print_things`, then finally the `prints_one_thing()` function. You can write that every time, or you can write `use` to import it. Now we can see the error that says that it's private:
```rust
// ⚠️
mod print_things {
use std::fmt::Display;
fn prints_one_thing<T: Display>(input: T) {
println!("{}", input)
}
}
fn main() {
use crate::print_things::prints_one_thing;
prints_one_thing(6);
prints_one_thing("Trying to print a string...".to_string());
}
```
Here's the error:
```text
error[E0603]: function `prints_one_thing` is private
--> src\main.rs:10:30
|
10 | use crate::print_things::prints_one_thing;
| ^^^^^^^^^^^^^^^^ private function
|
note: the function `prints_one_thing` is defined here
--> src\main.rs:4:5
|
4 | fn prints_one_thing<T: Display>(input: T) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
```
`function `print_one_thing` is private is easy to understand. It also shows us where to find the function. This is because once you start using `mod` you might also be using more than one file as well, and it can be hard to find things.
Now we just write `pub fn` instead of `fn` and everything works.
```rust
mod print_things {
use std::fmt::Display;
pub fn prints_one_thing<T: Display>(input: T) {
println!("{}", input)
}
}
fn main() {
use crate::print_things::prints_one_thing;
prints_one_thing(6);
prints_one_thing("Trying to print a string...".to_string());
}
```
This prints:
```text
6
Trying to print a string...
```
How about `pub` for a struct, enum, trait, or module? `pub` works like this for them:
- `pub` for a struct: it makes the struct public, but the items are not public. To make an item public, you have to write `pub` for each one too.
- `pub` for an enum or trait: everything becomes public. This makes sense because traits are about giving the same behaviour to something. And enums are about a selection between items, and you need to see them to select them.
- `pub` for a module: the first module will be `pub` because if it isn't pub then nobody can touch anything in it at all. But modules inside modules need `pub` to be public.
So let's put a struct named `Billy` inside `print_things`. This struct will be almost all public, but not quite. The struct is public so it will say `pub struct Billy`. Inside it will have a `name` and `times_to_print`. `name` will not be public, because we only want the user to create structs named `"Billy".to_string()`. But the user can select the number of times to print, so that will be public. It looks like this:
```rust
mod print_things {
use std::fmt::{Display, Debug};
#[derive(Debug)]
pub struct Billy { // Billy is public
name: String, // but name is private.
pub times_to_print: u32,
}
impl Billy {
pub fn new(times_to_print: u32) -> Self { // That means the user needs to use new to create a Billy. The user can only input times_to_print
Self {
name: "Billy".to_string(), // We choose the name - the user can't
times_to_print,
}
}
pub fn print_billy(&self) { // This function prints a Billy
for _ in 0..self.times_to_print {
println!("{:?}", self.name);
}
}
}
pub fn prints_one_thing<T: Display>(input: T) {
println!("{}", input)
}
}
fn main() {
use crate::print_things::*; // Now we use *. This imports everything from print_things
let my_billy = Billy::new(3);
my_billy.print_billy();
}
```
This will print:
```text
"Billy"
"Billy"
"Billy"
```
By the way, the `*` to import everything is called the "glob operator". Glob means "global", so it means everything.
Inside a `mod` you can create other mods. A child mod (a mod inside of a mod) can always use anything inside a parent mod. You can see this in the next example where we have a `mod city` inside a `mod province` inside a `mod country`. You can think of the structure like this: even if you are in a country, you might not be in a province. And even if you are in a province, you might not be in a city. But if you are in a city, you are in its province and you are its country.
```rust
mod country { // The main mod doesn't need pub
fn print_country(country: &str) { // Note: this function isn't public
println!("We are in the country of {}", country);
}
pub mod province { // Make this mod public
fn print_province(province: &str) { // Note: this function isn't public
println!("in the province of {}", province);
}
pub mod city { // Make this mod public
pub fn print_city(country: &str, province: &str, city: &str) { // This function is public though
crate::country::print_country(country);
crate::country::province::print_province(province);
println!("in the city of {}", city);
}
}
}
}
fn main() {
crate::country::province::city::print_city("Canada", "New Brunswick", "Moncton");
}
```
The interesting part is that `print_city` can access `print_province` and `print_country`. That's because `mod city` is inside the other mods. It doesn't need `pub` in front of `print_province` to use it. And that makes sense: a city doesn't need to do anything to be inside a province and inside a country.

Loading…
Cancel
Save