Выбрать главу

Modules can be mapped to a file/directory hierarchy. Let's break down the visibility example in files:

$ tree .

.

|-- my

| |-- inaccessible.rs

| |-- mod.rs

| `-- nested.rs

`-- split.rs

In split.rs:

// This declaration will look for a file named `my.rs` or `my/mod.rs` and will

// insert its contents inside a module named `my` under this scope

mod my;

fn function() {

println!("called `function()`");

}

fn main() {

my::function();

function();

my::indirect_access();

my::nested::function();

}

In my/mod.rs:

// Similarly `mod inaccessible` and `mod nested` will locate the `nested.rs`

// and `inaccessible.rs` files and insert them here under their respective

// modules

mod inaccessible;

pub mod nested;

pub fn function() {

println!("called `my::function()`");

}

fn private_function() {

println!("called `my::private_function()`");

}

pub fn indirect_access() {

print!("called `my::indirect_access()`, that\n> ");

private_function();

}

In my/nested.rs:

pub fn function() {

println!("called `my::nested::function()`");

}

#[allow(dead_code)]

fn private_function() {

println!("called `my::nested::private_function()`");

}

In my/inaccessible.rs:

#[allow(dead_code)]

pub fn public_function() {

println!("called `my::inaccessible::public_function()`");

}

Let's check that things still work as before:

$ rustc split.rs && ./split

called `my::function()`

called `function()`

called `my::indirect_access()`, that

> called `my::private_function()`

called `my::nested::function()`

A crate is a compilation unit in Rust. Whenever rustc some_file.rs is called, some_file.rs is treated as the crate file. If some_file.rs has mod declarations in it, then the contents of the module files would be inserted in places where mod declarations in the crate file are found, before running the compiler over it. In other words, modules do not get compiled individually, only crates get compiled.

A crate can be compiled into a binary or into a library. By default, rustc will produce a binary from a crate. This behavior can be overridden by passing the --crate-type flag to lib.

Let's create a library, and then see how to link it to another crate.

pub fn public_function() {

println!("called rary's `public_function()`");

}

fn private_function() {

println!("called rary's `private_function()`");

}

pub fn indirect_access() {

print!("called rary's `indirect_access()`, that\n> ");

private_function();

}

$ rustc --crate-type=lib rary.rs

$ ls lib*

library.rlib

Libraries get prefixed with "lib", and by default they get named after their crate file, but this default name can be overridden by passing the --crate-name option to rustc or by using the crate_name attribute.

To link a crate to this new library you may use rustc's --extern flag. All of its items will then be imported under a module named the same as the library. This module generally behaves the same way as any other module.

// extern crate rary; // May be required for Rust 2015 edition or earlier

fn main() {

rary::public_function();

// Error! `private_function` is private

//rary::private_function();

rary::indirect_access();

}

# Where library.rlib is the path to the compiled library, assumed that it's

# in the same directory here:

$ rustc executable.rs --extern rary=library.rlib --edition=2018 && ./executable

called rary's `public_function()`

called rary's `indirect_access()`, that

> called rary's `private_function()`

cargo is the official Rust package management tool. It has lots of really useful features to improve code quality and developer velocity! These include

   • Dependency management and integration with crates.io (the official Rust package registry)

   • Awareness of unit tests

   • Awareness of benchmarks

This chapter will go through some quick basics, but you can find the comprehensive docs in The Cargo Book.

Most programs have dependencies on some libraries. If you have ever managed dependencies by hand, you know how much of a pain this can be. Luckily, the Rust ecosystem comes standard with cargo! cargo can manage dependencies for a project.

To create a new Rust project,

# A binary

cargo new foo

# OR A library

cargo new --lib foo

For the rest of this chapter, let's assume we are making a binary, rather than a library, but all of the concepts are the same.

After the above commands, you should see a file hierarchy like this:

foo

├── Cargo.toml

└── src

└── main.rs

The main.rs is the root source file for your new project -- nothing new there. The Cargo.toml is the config file for cargo for this project (foo). If you look inside it, you should see something like this:

[package]

name = "foo"

version = "0.1.0"

authors = ["mark"]

[dependencies]

The name field under [package] determines the name of the project. This is used by crates.io if you publish the crate (more later). It is also the name of the output binary when you compile.