Mod seems to be counter-intuitive in rust

this is just me. i am unable to understand the usage of modules with mod keyword.

is mod used to import a file into another file?
like is it an alias to a file name?

1 Like

It is very different from other syntaxes that work on file inclusion. Rust doesn't. mod defines that a module exists. mod works the same way as fn, struct and enum. You create a definition in one place and don't repeat it.

i feel that the location where we declare mod is weird.

rust
// src/holder.rs

// A simple trait that "holds" a value of some type.
pub trait Holder<T> {
    fn push(&mut self, value: T);
    fn pop(&mut self) -> Option<T>;
    fn len(&self) -> usize;
}
File 2: queue.rs
rust
// src/queue.rs

mod holder;
pub use holder::Holder;

pub struct Queue<T> {
    data: Vec<T>,
}

impl<T> Queue<T> {
    pub fn new() -> Self {
        Self { data: Vec::new() }
    }
}

impl<T> Holder<T> for Queue<T> {
    fn push(&mut self, value: T) {
        self.data.push(value);
    }

    fn pop(&mut self) -> Option<T> {
        if self.data.is_empty() {
            None
        } else {
            Some(self.data.remove(0))
        }
    }

    fn len(&self) -> usize {
        self.data.len()
    }
}

here i thought mod should be mentioned in holder.rs file. but it gives me error when i do that. but no errors when i do it in queue.rs file

It's definitely not just you :'D From what I currently understand about how mod works. It's sort of like, your source files/sections are hidden by default. The compiler starts linking other files/namespaces starting from either main.rs or lib.rs. When you write mod something in either lib or main, you basically declare/define to the compiler that either a file or a separate namespace by that name exists. I was confused by how mod could be used to define a module either inline in a file or outside as a separate file. The mechanism clears up a bit when you combine it with the use keyword. So basically mod something defines that this seperate namespace EXISTS, and use something::SomeType makes that specific thing from a separate namespace visible to other namespaces.... or 'links' that new namespace to the other namespace.

2 Likes

so, it is more like extern keyword for C/C++ functions.

mod declares where a module exists, not that a file is a module.

Just like fn name or struct name makes the name exist where you wrote it, so does mod name make name exist where you wrote it.

Rust modules aren't files that say what they are. Rust defines named "items", and some of the items can be of a module type (other types are functions, structs, constants, static variables), and some of the modules can load their body from a file if needed.

#[path="/etc/passwd"]
mod name;

#[path="/etc/passwd"]
mod name2;

mod is discovered first, and then it looks for a file if needed. It's possible for the path to be anything. You can even make two different modules load the same file (it's not useful, but conceptually possible).

2 Likes

so first one is like #include?

I think i am confusing a lot..
plz answer this.

i have a file holder.rs with Holder trait. << this should also be in holder module.

i have 2 files queue.rs and stack.rs that has Queue and Stack structs and implements Holder traits. << queue and stack module respectively.

i have a main.rs file that runs a test program for both Queue and Stack.

how do i import holder module in queue.rs and stack.rs file and how do i import queue and stack module in main.rs file.

i tried it with my current understanding. but it is not working at all. :frowning:

:sob:

mod holder should get linked to the root (either lib.rs or main.rs) through a 'chain' (just my mental model). This can happen in various different ways based on how and where you have defined this mod.

src/lib.rs
mod queue;
src/queue.rs / src/queue/mod.rs-> Either a seperate file or queue/mod.rs
mod holder;

src/queue/holder.rs / src/queue/holder/mod.rs 
your code...

The modern structure looks like this (note that mod can be used to define modules inline a file too, so things can get confusing that way)

my_project/
 |-Cargo.toml
 |- src/
    |- lib.rs           <-- The Root
    |- queue.rs         <-- Parent module
    |- queue/           <-- Folder named exactly like the parent module
        |- holder.rs    <-- Child module

I think, in Rust module, you may find it easier to differentiate between creating structure of your modules and importing them for use.

So, in the main, you declare the structure like,

// main.rs
mod queue;
mod holder;

Then, your module structure will become,

+ crate (root)
  |- queue
  |- holder

Then you import it anywhere you want to use as

use crate::holder::Holder;

I hope it supplement above answer.

1 Like

i think you misunderstood one part.

holder is like an interface that is common to both queue and stack.

1 Like

Oh sorry, you had holder.rs in the same depth as lib.rs.

So basically define

src/lib.rs
pub mod holder;
pub mod queue;

src/queue.rs
use crate::holder::Holder;
<...code>

Experiment with pub, pub(crate), pub(super) etc to see what makes things visible granularly :'D I am still confused by how these work too :'D I just keep writing until I get the right privacy modifier that compiles :'D

1 Like

i got confused again.

now i have main.rs file

mod queue;
fn main() {
    let q = queue::Queue::new();
}

and in queue.rs file

struct Queue {
...
}
...

this is working fine even i don't declare any namespace kind of thing in queue. why is it inferring the file name now?

but when i create a holder.rs file

trait Holder {
    fn push(&mut self);
    fn pop(&mut self);
    fn peek(& self);
}

and try to import that in queue.rs file.

mod holder;

it is shooting me an error.

woah. it is really sophisticated.

so it is like i am helping rust to internally construct a model of how the files are organized so that the compiler can do a better job in finding them?

You can't do that, this won't compile because it's not directly 'linked' to main.rs.

src/
|- main.rs   (Contains: mod queue;)
|- queue.rs  (Contains: mod holder;)
|- queue/    <-- Create this folder!
    |- holder.rs

You declared the "namespace" in main.rs when you wrote mod queue.

Declare the module in main.rs or lib.rs, just as you did for queue, and it'll work. Modules are not "auto-discoverable", that's what consistentmilk-12 meant by "chaining".

1 Like

ahh. i got it.
i did it just now when you were writing. i was using it like import package in Java. but this is different.

and i am finding that it is like an index for the compiler to know about different modules.

and when i don't explicitly define a module like

mod holder {
...
}

the file name is taken as the module.

3 Likes

You should perhaps forget about the connection between modules and files. Yes, you're telling the compiler that a module exists, but it doesn't necessarily mean that such module was declared in a file with the same name.