rust mod.rs vs lib.rs
( Updated : October 23, 2021 )
🔥 DOWNLOAD LINK Links to an external site.
Clear explanation of Rust’s module system Rust modules vs files
Clear explanation of Rust’s module system Rust By Example The Rust Reference A Gentle Introduction to Rust Rust modules vs files
Rust changed the way modules work so that instead of having a module defined as / with other submodules in the /. › rust-by-example › mod › split. This declaration will look for a file named `` or `my/` and will modules mod inaccessible; pub mod nested; pub fn function() { println! It is encouraged to use the new naming convention as it is more consistent, and avoids having many files named within a project. The path attribute. Imagine the following directory structure: code/ `- - something/ `- If in you do mod something; , then it'll look in. › rust-lang › edition-guide › issues. In Rust , is no longer needed. can just be , and the submodule is still foo/ This eliminates the special name. Let's convince ourselves that's true by changing our src/ to this: Rust code. mod math { pub fn add(x: i32, y: i32) -> i32 { x + y }. General confusion about crates vs modules; and being in the same directory, but declaring different crates. Not understanding how to import stuff. Because boo/ can refer to other modules defined in boo , Update boo/ and add a new module - note that this is explicitly exported. (Without the pub. Rust's module system is surprisingly confusing and causes a lot of Here, the compiler looks for my_ or my_module/ in the. Build Targets — bin vs. Every Rust file ( .rs ) is a module, so the intention is not to map every component like a struct to your.
A while back, I asked on Twitter what people found confusing in Rust , and one of the top topics was "how the module system maps to files". I remember struggling with that a lot when I first started Rust, so I'll try to explain it in a way that makes sense to me. All that follows is written for Rust edition. I have no interest in learning or teaching the ins and outs of the previous version, especially because it was a lot more confusing to me. If you don't have one - add it now. A crate is basically a project. It has a Cargo. It doesn't have to be that precise path, you can add sections to Cargo. We can call cargo run to build AND run it, or just cargo build to build it. When building a crate, cargo downloads and compiles all required dependencies, putting temporary files and final build artifacts in the. Let's add a dependency on the rand crate to our Cargo. Our Cargo. For the rest of this post to make sense, you need to understand that rust modules are basically just namespaces - they let you group related symbols together, and enforce visibility rules. If we don't feel like writing rand::random all the time, we can bring it into our main module's scope. We also could've used a wildcard to import all the symbols exported by the rand crate's main module. But it didn't. It didn't even parse it. However, this is not how you usually include modules. By convention, if you have a mod directive without a following block, it does It's as simple as that. The confusing bit is that, depending on whether mod is followed by a block or not, it's either defined inline, or it includes another file. In particular, use never instructs the compiler to parse more files than it usually would. In our main. That's why, if we want to use add, we need to refer to it as math::add - which is a proper path from the main module to add. Note that if we were calling add from a different module, math::add might not be a valid path. However, there is an even longer path for add, which is crate::math::add - and that one will work from anywhere in our crate as long as the math module stays where it is. This made sense because math was a small module only one function , it didn't really need its own folder. But we could just as well change our file structure to this:. Let's say we want to add a sub function, and, because we arbitrarily enforce a maximum of "one function per file" limit, we want add and sub to live in their own modules. We'll just make it use add and sub:. But we need to make it aware of the add and sub modules as well. So math::add is not a valid path, because the math module exports nothing. The fact that math is in fact made up of two submodules is an implementation detail. We don't really want to export these modules - and we definitely don't want anyone importing those directly! We have successfully hidden away the implementation details of the math module - only the add and sub functions are exposed. Indeed, we don't use sub in main right now. What happens if we remove its from the use directive? The explanation is fairly simple. In the current state of the crate, sub is not exported to the rest of the world anywhere. So, we're asking the compiler to parse a source file, type check and borrow check it - but it the sub function doesn't even end up in the final executable. Even if we were to turn our crate into a library , it wouldn't be usable, since it's not exported from the entry point! We have a few options. If our crate is going to be both a library and a binary, we can simply make the math module public. Or, we can remove the sub function after all, we don't need it yet. If we know we're going to use it later, we can turn off the warning for that specific function:. It's too easy to forget about dead code once you add this annotation. Remembering to grep for unused is hard! And that's what source control is for. Nevertheless, the option is there if you want it. And the answer is: it doesn't matter. The only harm you can do with an overzealous wildcard use e. But the compiler parses all the files anyway, and excludes the parts you don't actually need via dead code elimination , regardless of what's in scope. Let's say we want the math module to have a module-level constant that enables or disables logging. Note: this is a terrible way to do logging, I just can't think of another silly example right now. Note that a module always has access to its parent's scope via super:: - even the unexported items. DEBUG is not pub , but we can use it just fine in add. We could even use wildcards, assuming our submodules only export symbols that we also want to export ourselves:. Well, there is no direct path between sibling modules add and sub , for example. Just because the add and sub modules happen to have the same parent, doesn't mean they share namespaces. We also definitely should not use a second mod. The add module already exists somewhere in the module hierarchy. If we want to access add , we have to go through the parent, like everybody else. Note: a function is its own scope, so this use will not affect the rest of this module. As crates get complicated, so do their module hierarchies. Instead of re-exporting everything from the crate's entry point, some crates curate a set of "most useful" symbols and export them from a prelude module. Would bring something in scope that is called serde , that would shadow the serde crate , for example. I hope this clarifies rust modules vs files for some folks. You can let me know on Twitter if you have questions. Thanks for reading! Become a Patron. Home Articles Series About. Log in. Important note All that follows is written for Rust edition. What's a crate? Let's say we're making a binary: cargo new --bin or cargo init --bin in an existing folder will generate the Cargo. Compiling modules v0. To learn more, run the command again with --verbose. If you liked this article, please support my work on Patreon! Looking for the homepage? Another article: A terminal case of Linux.