Modules don't make any sense (Error: cannot declare a new module at this location)


#1

Rust’s modules confuse me. I’ve read the book and a couple of answers which seem to boil down to “You can do mod foo {mod bar {}}, and mod foo; gets content from foo.rs or foo/mod.rs. Easy-peasy!”

I’ve had:

  • lib.rs
  • bar.rs

and lib.rs contained:

mod bar;
use bar::bar_stuff;

and it all worked, but the lib.rs file got too big for my liking, so I wanted to cut and paste its body elsewhere. I’ve changed it to:

  • lib.rs containing mod foo; pub use foo::*;
  • foo.rs containing body of previous lib.rs, i.e. mod bar; use bar::bar_stuff;
  • bar.rs unchanged

And this gives me:

mod bar;
error: cannot declare a new module at this location

The same code that worked in lib.rs stopped working when I’ve moved it to another file in the same directory. It’s exactly the same declaration that compiled fine in lib.rs! :pulls hair:

The error suggests to me that lib.rs is somehow special and only it is allowed to declare other modules, but Rust documentation shows that modules can easily be nested recursively. What am I missing here? I don’t understand why I can’t declare it at “this location”.

I suspected that’s because Rust requires me to use foo/mod.rs and foo/bar.rs file naming scheme, so I’ve tried:

  • lib.rs containing mod foo; pub use foo::*;
  • foo/mod.rs containing body of previous lib.rs, i.e. mod bar; use bar::bar_stuff;
  • foo/bar.rs unchanged

In foo/mod.rs:

mod bar; // compiles!
use bar; // compiles!
use bar::bar_stuff; // error: unresolved import bar::bar_stuff Maybe a missing extern crate bar? [E0432]"

Argh! I don’t get it. How can it work (use bar) and not work (use bar::bar_stuff) at the same time?

  1. Is mod in lib.rs somehow special and magical?
  2. Why mod declarations stop working if I move them from lib.rs to another file in the same directory?
  3. Can I keep using multiple modules and never ever need to use foo/mod.rs naming scheme? (I don’t want to have to work with many files all named mod.rs).

#2

And this is surprising to me (in src/foo/mod.rs, the 3rd option):

mod bar;
use bar; // what is this using, if it's not the bar module?
use bar::bar_stuff; // error: unresolved import `bar::bar_stuff`
mod bar;
use self::bar as bar; // error: import `bar` conflicts with existing submodule [E0256]

So there is an existing submodule bar, but it doesn’t have content of the bar submodule!? Do I really have to edit all my existing code and rename bar::… to self::bar::…?


#3

Modules can be hard because you’re messing with files and their locations at the same time. Let’s see…

No, it is not.

The issue here isn’t the declarations, it’s where your files are. Here’s the full error:

src/foo.rs:1:5: 1:8 error: cannot declare a new module at this location
src/foo.rs:1 mod bar;
                 ^~~
src/foo.rs:1:5: 1:8 note: maybe move this module `foo` to its own directory via `foo/mod.rs`
src/foo.rs:1 mod bar;
                 ^~~
src/foo.rs:1:5: 1:8 note: ... or maybe `use` the module `bar` instead of possibly redeclaring it
src/foo.rs:1 mod bar;
                 ^~~

ie, you’re trying to keep your directory strutcture flat, but your modules are forming a hierarchy. Let’s move some files around:

$ mkdir src/foo
$ mv src/bar.rs src/foo/
$ mv src/foo.rs src/foo/mod.rs

Now we get a slightly different error:

src/foo/mod.rs:2:5: 2:8 error: unresolved import `bar::bar_stuff`. Maybe a missing `extern crate bar`? [E0432]
src/foo/mod.rs:2 use bar::bar_stuff;
                     ^~~

use statements are relative to the root. Before, this line of code was in the root, but now it’s not. If you move a use statement, what you import will have to change. You can do two things:

use ::self::bar::bar_stuff;

Or

use foo::bar::bar_stuff;

If you’re nesting things, you’ll have to use it eventually. If you keep it all flat, then you don’t. If both mod foo and mod bar were in your lib.rs, they’d correspond to src/foo.rs and src/bar.rs.


#4

/* edit: that works. I must have tested it wrong the first time */

lib.rs:

mod foo;
mod bar;

foo.rs

use bar; 

#5

Ahhh, I didn’t expect that. Because std::something works only in root, and in modules I need ::std::something. I’ve been adding use ::std as boilerplate to all modules, so that I can use std::something consistently everywhere.

And having use ::std (now I see that with a needless ::) in every module except root made me think use is module-relative; I expected mod to be absolute, since that “works” for me only in root :slight_smile:


#6

Yup, it happens :smile:


#7

Thank you for your help!

I’ve realized I’ve also made life difficult for myself by having both bin.rs and lib.rs, and cargo test compiles both, so I was getting weird errors until I fixed both targets.