What’s the difference between mod and Rust?

Asked

Viewed 85 times

6

I understand the workings of the keyword mod, explained in this matter. For users of Python, mod works exactly like the keyword import. That is, to use functions and other objects defined in a file foo.rs, just add, on top of main.rs, the command mod foo.

However, this finding caused me some confusion because, until then, I believed that the keyword use was the Rust equivalent of import in Python.

Even, when I try to do the same procedure above with use, I am faced with the following mistake::

1 | use foo;
  |     ^^^^^ no external crate `foo`

What is the keyword for use, then?

  • 1

    No, you don’t understand, use it looks like import in Python, but not exactly the same. mod has no Python equivalent because the mechanism looks but is not equal.

1 answer

6


The difference is that they do different things. : D

mod

The keyword mod declaring a module in Rust. Modules are used to control scope and privacy. For an introduction to the concept of modules, see here.

The mod can be used in two different ways to create modules:

  1. Explicitly defining a block

    //# Arquivo `main.rs`
    
    mod math {
        pub fn add(a: i64, b: i64) -> i64 { a + b }
        pub fn sub(a: i64, b: i64) -> i64 { add(a, -b) }
    }
    
    // Podemos utilizar `math::add` e `math::sub` aqui.
    

    Above, we created a new module, qualified as math. Outside of this module, to use one of its exposed functions, it is necessary to use the module name.

    Notice that inside math, it is possible to refer to the function add only as add (as I did to implement the function sub). But, from outside the module, as said above, it is necessary to use math::add.

  2. Defining via another file

    //# Arquivo `math.rs`
    
    pub fn add(a: i64, b: i64) -> i64 { a + b }
    pub fn sub(a: i64, b: i64) -> i64 { add(a, -b) }
    

    And:

    //# Arquivo `main.rs`:
    
    mod math;
    
    // Podemos utilizar `math::add` e `math::sub` aqui.
    

    Note that from the perspective of main.rs, nothing has changed. In it, as in the previous example, a new module has been defined, qualified as math. The difference is where the contents of the module have been defined.

    In this case, where mod is used without defining a block explicitly, it will create a new module with the given name and search the contents of the module in a file with corresponding name.

    Note that the module nay is defined in the file math.rs. That is, the file math.rs nay is the definition of a module. This is often a point of confusion in Rust since some languages (such as Javascript) operate differently. The definition of the module occurs in main.rs, through the directive mod math.

With this, we can conclude that a module in Rust is created by the directive mod. The contents of the file can be set directly, with a block following mod <name>. Can also be set in the file <name>.rs.


use

The directive use is, when allied to the mod, a point of confusion. In many languages (such as Javascript and Python), the concept of modules is usually associated with a single keyword, such as import. Rust takes a slightly different approach and so it may seem "strange". But it’s actually quite simple. Let’s see...

We have seen that a module can be defined using the keyword mod. From that moment, we will be able to access the contents of the module using a module path.

In the above examples, main.rs, the way to get to the function sum, module math, is math::sum. If there were several nested modules (which is common in Rust), it would be something like std::fs::read_to_string. In this case, it is a function called read_to_string, inside the module fs which, in turn, is inside the module std.

So what’s the point of use?

I’ll tell you what the use it is not. The use is not a mechanism for importing modules. This means that the use does not serve to bring anything new to the scope. So much so if you try to use use with something that is not in scope, will give error.

The use serves to shorten the way member(s) of a module.

Regarding the example of the beginning of the question, if, in main.rs, the function was used math::add several times, it would be repetitive to have to type math::add each time. In that case, one could do:

mod math; // Define o módulo `math` com os membros de `math.rs`.

// Elevo o "escopo" de `add`. Note que estou, essencialmente,
// encurtando o caminho até o membro (no caso, função) `add`:
use math::add;

// Agora posso chamar como:
math::add(1, 2); // Válido, caminho completo. OU:
add(1, 2); // Também válido (caminho encurtado definido pelo `use`).

Notice that in doing use math::add, shorten the path to access the function add. Now, no more need to prefix with math::. Only add that’s enough.

In relation to std::fs::read_to_string, usually the programmer does this:

use std::fs;

// Pode utilizar como:
std::fs::read_to_string("./Cargo.toml");
fs::read_to_string("./Cargo.toml"); // Faz uso do encurtamento criado pelo `use` acima.

But it could also be:

use std::fs::read_to_string;

// Pode utilizar como:
std::fs::read_to_string("./Cargo.toml");
read_to_string("./Cargo.toml"); // Faz uso do encurtamento criado pelo `use` acima.

So the use (more details here) can be used as a means to shorten module paths. In short, with or without the use, we would still be able to access the function read_to_string. The difference is that the use allows us to shorten this path. It is extremely useful when we use the same path several times and if we want to avoid repetitions.

Cannot be used use on some name that does not exist in the current scope.

That’s why it gave error (as shown in the question) to do this:

use foo;

What is foo?

A module is required foo defined in the scope to shorten the path to one of its members.

Lacked a definition mod foo before the use foo. Or a Crate foo defined as project dependency.

Crates has its highest name set for all modules in the project. That’s why you can use it, for example, use std::fs without making a mod std before. The std is a standard Crate of Rust. All Crates you define as project dependency are also exposed to your modules.


Summary of the opera

What brings a new name to the scope is the mod, and not use. The use it doesn’t matter at all.

You need to define, with the directive mod, the modules you will use. Crates, as the std Rust or those you define as project dependency, are set automatically.

The use is used to abbreviate a path that leads to a member of some module.

  • 1

    Excellent answer! So the comparison with the import of Pyhon only makes sense with mod because it’s just the mod that actually adds the module into the scope. That means the closer we could compare the use with Python would be in the form import mod.func as func which only corresponds to the use of mod and use together, correct?

  • 1

    Yes, @fernandosavio, I’m not an avid Python connoisseur, so I can’t answer comparatively with certainty, but I think it’s a fair comparison. And yes, it’s the mod which, in fact, brings something new to the scope. An analogy for the use is to "unearth" something that lies within some module already present in the scope (imported "implicitly", as Crates, or modules imported explicitly via mod).

Browser other questions tagged

You are not signed in. Login or sign up in order to post.