I love rust, but one thing about modules is aweful!

Hello Guys and Girls of the Rust Community.

I started with Rust a few days ago and I think I'm quite successful, cause I rebuilt a Backup system which just relies on XML2 (C) libxenserver (C) and LVM, Zipping to make backups of our XenServer infrastructure. The old code was written in Python 2 and was really aweful to run since some XenServers are still on Py 2.4 / Py 2.6, however rust works brilliantly even with a simple http-interface which uses sqlite3 for his user database.

However there is one thing I found really aweful..
Why do I need to write

mod mymodule;
use mymodule::my_code;

I mean I just want to call code inside my own module space I don't want to call any extern crate here (there the explicit extern crate will make sense). However I'm calling code under src/mymodule.rs or under src/mymodule/mod.rs so it's already there the explicit "mod" makes no sense. I also found no way to just write something like:

use mymodule;
// so that I can write:
mymodule::function1();

The documentation around modules here is really lacking. Still most things in rust just works and since I'm coming from scala/jvm and some python I really like the immutable / functional style of a compiled language, especially the FFI (even that header files are hard to integrate if you are really new, but rely on some native C code)

1 Like

You are looking for mymodule::function() I believe.

Nah that was just a typing error, I meant mymodule::function1() however I think there is also ::mymodule::function1() however I don't know if that could be used, as said its undocumented.

Ah then I guess you're missing use mymodule? I do little enough work in lib.rs itself that I haven't often run into this issue.

no.
Curently I can call code in lib.rs and main.rs however it's awful, either I write:

mod mymodule;
use mymodule::function1;
// calling:
fn main {
   function1();
}

or I could write:

mod mymodule;
fn main {
   ::mymodule::function1();
}

however the second is not documented. And i really don't like the "mod mymodule" if I only can use a reference with ::mymodule instead of mymodule it also won't work to have mod mymodule; and use mymodule;
I definitly dislike this style especially when working with lot's of c functions the code starts to look really really ugly.

If you really want to avoid namespacing, you could use an *. Personally, I like the namespace aspect of modules.

I've just tried and it works:

mod outer {
    mod inner {
        pub fn foo() { }
    }
    
    pub fn bar() {
        inner::foo();
    }
}

fn main() {
    outer::bar();
}

I mean, if you have mod mymodule;, use self::mymodule; is implicit.

try that in main.rs without the module in the same file.

It doesn't matter if the module is in the same file or not. Yes I've tried. Still works.

::foo::bar, is absolute, referencing from the crate root, where as foo::bar is referencing it from inside the current module. For example:

mod mymodule {
    pub fn function1() {}
}

mod another {
    fn f() {
        // We can't use mymodule::function1 here.
        // mymodule doesn't exist in currect scope,
        // but it exists in the crate root, 
        // so this works:
        ::mymodule::function1();
    }
}

But you can also "import" it locally by using use:

mod another {
    // brings mymodule into scope for this module
    use mymodule;

    fn f() {
        // we no longer need absolute path for mymodule
        mymodule::function1();
    }
}

As for needing both mod and use. you don't need add use. You can reference everything from the crate root if you choose. It just would make for very messy code!

As @gkoz says, it still works. If you think it doesn't for you, please post the full code for all files you are testing with and we can have a look.

Seems like you badly misunderstand modules in Rust. They are not like namespaces, they are a little closer to C's #include but with better semantics.

When you write your code, you implicitly create a crate, and your main.rs is the root of your crate. This is an entry point to your whole application, and when you put mod mymodule; into it, it just means something like #include "mymodule.c"; in C, that is you declare you have a module, and the compiler should look for it in mymodule.rs or mymodule/mod.rs. Thanks to holy Ferris you need to do it only once in the crate's root, unlike in C. Once the module is declared (read "included from other file") in the crate root, you can use it from any module in the same crate however you want. And, unlike in C, all files included with mod, are put into separate "namespace", so names from these other files don't mess up with your current file.

When you write use mymodule;, you don't declare ("include") it, you just tell compiler to move some names from the module into your current namespace, so you don't ::have::to::write::full::path::to::your::names::in::some::module::just::to::call::some::function(). You don't have to use thingies from other modules with use, you can ::always::use::full::paths, but it's just annoying.

Hope it helps.

4 Likes

hm I understood that it isn't like that, however I found it aweful, when I need to call like 20 functions in a single file.
it's like writing 20 times use that's making it really aweful especially with the bindgen tool which creates everything at a top level module so I need to write ::mymodule::function1(); or I would have the need for 20x use mymodule::function1;
as said FFI is great and some things are easy to adpot. even working with c_void's all over the place is easier than I thought thanks to CStr, CString.

However as said the module system is really really hard to read. I like the main.rs and lib.rs entrypoint since it has a convention, not like Golang where you could make any file a go entrypoint, still I somehow prefer their way of imports (for internal packages).

I mean mostly it would be easier to have a good Editor which could manage things for me or helping me with these imports. However there is only racer, yet. Which isn't that much of an help.

Also I think it's just something that the documentation should have a 'more complete' explanation of why and how and especially more examples, the examples at Crates and Modules are ok, but not good! Especially since these examples almost using external crates!

I'm not quite get you, what do you mean by "20x use mymodule::function1;"? You just mention it at the top of your module (most likely a rust file you are going to call the function in) and just go on with it. More like use once per file, not once per call site. Or use can just use mymodule::*; and get whole bunch of functions from the mymodule crowding up in your current namespace.

You can also write

use mymodule::{
    function1,
    function2,
    function3
};

to import multiple function, or

use mymodule::*;

to get everything. Just FYI.

1 Like

[quote="schmitch, post:13, topic:2930"]
::mymodule::function1(); or I would have the need for 20x use mymodule::function1;
[/quote]Nothing is making you put these leading :: there. Just use a correct import.

Talking about FFI, I find it neat when you make the use of extern fns explicit like this

use some_module_or_crate as ffi;
//...
    ffi::function1();
1 Like

Just to finish this comparison with C, you would need to #include "mymodule.h" in every C file you are going to use your functions in, like use mymodule::*;, but without nice options of separate namespaces.

when I import everything is my binary size affected or not?

Of cause it is. That is the following:

mod a {
  fn hello() { println!("Hello, world"); }
}

fn main() {
  a::hello();
}

is the exactly the same as:

// a.rs
fn hello() { println!("Hello, world"); }

//-----------------
// main.rs
mod a;

fn main() {
  a::hello();
}

That is externalizing modules into separate files is a kind of syntax sugar for direct inline modules form.

Or do you mean useing modules? Then no, modules are already loaded with mod, so useing them again and again doesn't affect binary size.