Modules and Using Them

Hello,

Not sure if this is the right place so feel free to delete if it isn't, but i was just looking for somewhere to ask for some help

I've started learning Rust (2 days ago..) but i'm already very familiar with programming, but i've been having problems trying to declare and use modules, what i have is:

// ./src/main.rs
#[path = "server.rs"]
mod server;
//mod response;
//mod request;

fn main () {
    let options = server::HttpOptions {
      hostname: String::from("0.0.0.0"),
      port: 1334
    };
    server::listen(options)
}
// ./src/server.rs
use std::net::TcpListener;
use std::thread;
#[path = "request.rs"]
mod request;

pub struct HttpOptions {
  pub hostname: String,
  pub port: u32
}

pub fn listen(options: HttpOptions) {
        let port_str: String = options.port.to_string();
        let address = format!("{}:{}", options.hostname, port_str);
        let listener = TcpListener::bind(address).unwrap();

        for stream in listener.incoming() {
            let stream = stream.unwrap();

            println!("Connection established!");
            //handle_request(stream); single threaded
            thread::spawn(|| {  // multi threaded
                        request::handle_request(stream);
            });
        }
}

This works fine, but as soon as I place HttpOptions and listen inside a mod server {, i get errors:

error[E0433]: failed to resolve: use of undeclared type or module `thread`
  --> src/server.rs:22:13
   |
22 |             thread::spawn(|| {  // multi threaded
   |             ^^^^^^ use of undeclared type or module `thread`

error[E0433]: failed to resolve: use of undeclared type or module `request`
  --> src/server.rs:23:25
   |
23 |                         request::handle_request(stream);
   |                         ^^^^^^^ use of undeclared type or module `request`

error[E0433]: failed to resolve: use of undeclared type or module `TcpListener`
  --> src/server.rs:15:24
   |
15 |         let listener = TcpListener::bind(address).unwrap();
   |                        ^^^^^^^^^^^ not found in this scope

error[E0422]: cannot find struct, variant or union type `HttpOptions` in module `server`
 --> src/main.rs:7:27
  |
7 |     let options = server::HttpOptions {
  |                           ^^^^^^^^^^^ not found in `server`
  |
help: consider importing this struct
  |
1 | use crate::server::server::HttpOptions;

But i am already using the std::<...> packages, and none of my code is a crate - i try do use server; but it's just looking for a server crate

The following code:

mod server;

is equivalent to this:

mod server {
    /* contents of server.rs */
}

So putting a second mod server { ... } inside of server.rs is usually not what you want. That will create a submodule with the path server::server.

use will import a name into the current module's namespace only. This means it must appear in the same module where you refer to the imported name. When you use mod to create a new module, it starts with a fresh namespace, without any of the imports from its parent module.

use std::thread;
// The name `thread` is in scope here...
mod foo {
    // ...but not here, unless we `use` it again.
}

Every Rust library or executable program is a crate. (“Crate” is just Rust terminology for a single compilation unit.) The crate keyword in a path refers to the root or top-level module of the current crate. (This module is normally found in lib.rs for a library crate, or main.rs for an executable crate.)

So crate::foo::bar lets you refer to an item in the current crate by its absolute path (i.e., starting from the crate's root module).

1 Like

// ... but not here, unless we 'use' it again
Interesting

Thanks for the reply, i think i'll just avoid using mod <name> { ... }. to encapsulate a files logic, as it just feels like too much confusion, and seems to be no need if a file is kind of already a module

So crate::foo::bar lets you. refer to an item
Ahh ok, i just had a play and figured out how that works, eg i declare my modules in main.rs (mod server.rs; mod request.rs; etc), and in my server.rs i can just do use crate::request;

@ebebbington there are two different things that mod mod_name; do, and I think that's the source of many confusions:

  • They allow to have code located in another file, like include!("path/to/file.rs"); does.

  • But they also bundle that code within a module. And modules create their own namespace, which is a property that goes in both directions: to refer to items inside that module you need the module_name:: path prefix, and to refer to items from the parent module (the file containing the mod ... declaration), you need the super:: path prefix. In that regard, modules behave very similarly to a filesystem.

    • use ... "statements" then allow to "skip" using a special path prefix for the used items: we say these items have been brought into the scope of the place where the use ... "statement" was located. And that scope, in and on itself, is namespaced too.

      Back to the filesystem analogy, use "statements" are the equivalent of a symlink.

    • File system analogy:

So, in order to understand this characteristics, it is better to dissociate those two things: try and refrain yourself from using mod mod_name; (that is a mod with a semicolon instead of { ... }). And instead, use inline modules mod mod_name { /* mod contents here */ }, and include!(); statements (ideally independently, so that you understand stuff better).

In that direction, I would even recommend to start having all the code bundled within a single file, and only after such single-file code works, start extracting stuff with those include!s.

use ::std::net::TcpListener;
use ::std::thread;

mod server {
    pub struct HttpOptions {
        pub hostname: String,
        pub port: u32,
    }

    pub fn listen (...) {
        ... thread::spawn ( ... )
    //      ^^^^^^ no item `thread` in the scope of `server::*`
    }
}

Finally, try to have code (that compiles!) using the pattern:

mod mod_name {
    include!("mod_name.rs");
}
// or
mod mod_name {
    include!("mod_name/mod.rs");
}

And only once you reach that point, you can replace either with the mod modname; shorthand.

See also: