Modules

Cargo dependencies

Anything that's a dependency in Cargo.toml will be directly accessible by using the name of the crate, without the need of a use keyword:

[dependencies]
rand = "0.7.0"

#![allow(unused)]
fn main() {
let random_boolean = rand::random();
}

Include module from another file

If there is a math.rs file exposing a single function:


#![allow(unused)]
fn main() {
pub fn add(x: i32, y: i32) -> i32 {
    x + y
}
}

You can access from main.rs in the same folder:


#![allow(unused)]
fn main() {
mod math {
    include!("math.rs");
}
let result = math::add(1, 2);
}

There is a shorthand available that is used as convention:


#![allow(unused)]
fn main() {
mod math
let result = math::add(1, 2);
}

use keyword

The only purpose of use is to bring symbols into scope, making things shorter.


#![allow(unused)]
fn main() {
use math::add;
let result = add(1, 2)
}

mod.rs

A mod.rs file is a special file, the folder that it's in is considered the root and it can then expose paths from other files e.g.

- main.rs
- math/
    mod.rs
    add.rs

src/math/mod.rs:

mod add;

pub use add::add;

src/math/add.rs:


#![allow(unused)]
fn main() {
pub fn add(x: i32, y: i32) -> i32 {
    x + y
}
}

lib.rs

Putting public code inside a lib.rs file will be the entry point for the binary code inside main.rs:

src/lib.rs


src/main.rs

use libtest::math::add;

Absolute and relative paths

  • crate:: - / = root of current crate
  • super:: - ../ = from parent module, where root of crate is also considered a module
  • self:: - ./ = from self, so things like pub use self::{add::*} can be used in mod.rs

#![allow(unused)]
fn main() {
pub mod front_of_house {
		// Doesn't have to be public because it's a sibling
    mod hosting {
				// Needs to be public as it's a child we want to call from outer scope
        pub fn add_to_waitlist() {}
    }

    pub fn eat_at_restaurant() {
        // Absolute path
        crate::front_of_house::hosting::add_to_waitlist();
				// Relative path
        hosting::add_to_waitlist()
    }
}
}

Structs

Struct fields are private by default, you can't read or write from a parent. the summer() function constructs and returns a breakfast struct, but the private field still can't be used directly from any parents.


#![allow(unused)]
fn main() {
pub mod back_of_house {
    #[derive(Debug)]
    pub struct Breakfast {
        pub toast: String,
        seasonal_fruit: String,
    }

    impl Breakfast {
        pub fn summer(toast: &str) -> Breakfast {
            Breakfast {
                toast: String::from(toast),
                seasonal_fruit: String::from("peaches"),
            }
        }
    }
}

pub fn eat_at_restaurant() {
    let mut meal = back_of_house::Breakfast::summer("Rye");
    meal.toast = String::from("Wheat");
    println!("I'd like {:?} please", meal);
}
}

Enums

Enums are public by default


#![allow(unused)]
fn main() {
mod back_of_house {
    #[derive(Debug)]
    pub enum Appetizer {
        Soup,
        Salad,
    }
}

pub fn eat_at_restaurant() {
    let order1 = back_of_house::Appetizer::Soup;
    let order2 = back_of_house::Appetizer::Salad;

    println!("order1: {:?}\norder2: {:?}", order1, order2);
}
}

Use

Idiomatic to only bring in the parents of functions, so it's clear where they come from


#![allow(unused)]
fn main() {
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {
            println!("Adding to waitlist")
        }
    }
}

// Not idiomatic
use front_of_house::hosting::add_to_waitlist;
pub fn eat_at_restaurant() {
    add_to_waitlist();
    add_to_waitlist();
    add_to_waitlist();
}

// Idiomatic
use front_of_house::hosting;
pub fn eat_at_restaurant() {
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
    hosting::add_to_waitlist();
}
}

Structs, enums, and other types it's OK to bring in the full path

use std::collections::HashMap;

fn main() {
    let mut map = HashMap::new();
    map.insert(1, 2);
}

As

If two things have the same name, the 'as' keyword can be used.


#![allow(unused)]
fn main() {
use std::fmt::Result;
use std::io::Result as IoResult;

fn function1() -> Result {
    // --snip--
}

fn function2() -> IoResult<()> {
    // --snip--
}
}

pub use

This will export the used path, so in the example below hosting will be available

// lib.rs
mod front_of_house {
    pub mod hosting {
        pub fn add_to_waitlist() {}
    }
}

pub use crate::front_of_house::hosting;

// main.rs
use restaurant::hosting;
fn main() {
    hosting::add_to_waitlist()
}

Nested paths

use std::{collections::HashMap, vec};

fn main() {
    let mut map = HashMap::new();
    let mut vec = vec![1, 2, 3, 4];
    vec.push(10);
    map.insert(1, 2);

    println!("{:?}\n{:?}", map, vec);
}

Over the top example of nested paths

use std::{
    fs::File,
    io::{
        self,
        prelude::{Read, Seek},
        SeekFrom,
    },
};

fn main() -> io::Result<()> {
    let mut f = File::open("./src/test.txt")?;
    let mut buffer = [0; 10];
    f.seek(SeekFrom::End(-10))?;
    let n = f.read(&mut buffer)?;
    println!("The bytes: {:?}", &buffer[..n]);
    Ok(())
}

Glob pattern

* brings in everything without having to suffix it

The above could be written as


#![allow(unused)]
fn main() {
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::io::SeekFrom;
}

Which is much nicer to look at and understand

Include another file

Putting mod in front of a filename


#![allow(unused)]
fn main() {
// src/front_of_house.rs OR
// src/front_of_house/mod.rs
pub fn add_to_waitlist() {
    println!("Wow cool yo")
}

// src/lib.rs
// mod will look for file front_of_house.rs or front_of_house/mod.rs
mod front_of_house;
pub use front_of_house::add_to_waitlist;

pub fn eat_at_restaurant() {
    add_to_waitlist();
}
}

Note that prelude is used as a convention for glob imports, basically denoting that these are all useful modules that can safely be imported together.