How to properly import other files in rust - rust

I'm trying to understand how to properly setup imports, with two goals in mind:
Every script should be compilable using rustc
Building via cargo is supported swell.
With absolute paths it seems to work.
I cannot get it to work with relative paths.
However it seems wrong to use absolute paths.
Whats the correct way to handle imports?
Lets take two examples into account:
1. Example
src
├── main.rs
├── root
│   └── nested.rs
└── root.rs
abs paths
// main.rs:
#[path="root/nested.rs"]
mod nested
#[path="root.rs"]
mod root
// root.rs:
#[path="root/nested.rs"]
mod nested
rel paths
// main.rs:
mod root;
use crate::root::nested;
// root.rs --\> cannot be compiled using `rustc`.
pub mod nested;
2. Example
src
├── main.rs
├── notnested.rs
└── root.rs
abs paths
// main.rs:
#[path="notnested.rs"]
mod notnested
#[path="root.rs"]
mod root
// root.rs:
#[path="notnested.rs"]
mod notnested
rel paths
// main.rs:
mod root
mod notnested
// root.rs --\> cannot be compiled using `rustc`.
use crate::notnested;

I've got a repo here: https://github.com/cadolphs/aoc_2022/tree/main/src
I used Rust for the Advent of Code 2022. In this project, I have the main.rs for the binary. I have individual modules for each day of the challenge.
Some of these modules are single-file modules (day1.rs) and other modules are in directories like day11. Note that the "entry point" to a module in a folder should be called mod.rs for the import to work normally.
This structure should work for what you're trying to do.

Related

error[E0601]: `main` function not found in crate for utilitary file for a custom binary

I have a custom binary called cli.rs and a file clap.rs with some utilities for clap.rs:
src
bin
cli.rs
clap.rs
where clap.rs just provides me with the clap definitions:
pub fn get_matches() -> ArgMatches {
}
fn main() {}
so I can import them into cli.rs.
However if I take out fn main from clap.rs I get
error[E0601]: `main` function not found in crate `clap`
I don't want clap.rs to have a main function, I just it to simply be a utils file for the binary cli.rs
From the cargo book about package layout:
Cargo uses conventions for file placement to make it easy to dive into a new Cargo package:
.
// ...
├── src/
| | // ...
│ └── bin/
│ ├── named-executable.rs
│ ├── another-executable.rs
│ └── multi-file-executable/
│ ├── main.rs
│ └── some_module.rs
| // ...
[...]
The default executable file is src/main.rs.
Other executables can be placed in src/bin/.
[...]
If a binary, example, bench, or integration test consists of multiple source files, place a main.rs file along with the extra modules within a subdirectory of the src/bin, examples, benches, or tests directory. The name of the executable will be the directory name.
By putting both cli.rs and clap.rs into bin/, you told Cargo that you have two binaries: One named cli, one named clap.
This package layout should work for you:
src
bin
cli
main.rs (that's your cli.rs)
clap.rs
Alternatively, you could also put clap.rs into a lib crate (by putting it inside lib/ and then putting pub mod clap; inside lib.rs). Note however that you then need to reference get_matches() as <your_package_name>::clap::get_matches() so that the compiler knows you are not referencing something from your binary crate, but something from your library crate.

How to access nested modules/files from nested directories

Let's say that I have this file structure in my project:
.
├── Cargo.lock
├── Cargo.toml
├── src
│   ├── hotel
│   │   ├── guest.rs
│   │   ├── hotel_manager.rs
│   │   └── hotel.rs
│   ├── lib.rs
│   └── main.rs
In hotel_menager I declared only that I will use hotel and guest module:
pub mod hotel;
pub mod guest;
Then inside lib.rs I declared that I will use hotel_manager module:
pub mod hotel_manager;
and in the end I wanted to use hotel and guest in my main.rs binary but I am getting this error :
file not found for module hotel_manager
Then I thought that maybe I should use use but it also didn't help at all:
use crate::hotel::hotel_manager;
unresolved import crate::hotel.
My Cargo.toml:
[package]
name = "learn_file_spliting"
version = "0.1.0"
authors = ["kaczor6418 <test#test.com>"]
edition = "2018"
[lib]
name = "lib"
path = "src/lib.rs"
[[bin]]
name = "main"
path = "src/main.rs"
How can I use this nested module in lib.rs and then use re-exported modules inside main.rs ?
I know that if I create hotel_manager in /src directory then everything will work, but I want to have this hotel_manager module inside nested directory and learn how to use nested modules.
In Rust, you can't create a module by simply having it exist in the directory tree. You have to explicitly declare it with mod module_name; statement. This doesn't only declare that a module named module_name is used, it declares that such a module exists at all.
pub mod hotel_manager; doesn't work in src/lib.rs because this tries to creates a submodule named hotel_manager in the crate's root module, which would have to exist in either src/hotel_manager.rs or src/hotel_manager/mod.rs -- and neither exist.
Importing crate::hotel::hotel_manager in src/lib.rs doesn't work because you have never declared a pub mod hotel; in src/lib.rs, and therefore, no module named crate::hotel exists at this point.
Basically, for the hotel_manager module to exist at src/hotel/hotel_manager.rs, it would have to be declared as a submodule of the a crate::hotel module, which itself would need to be declared in the root module.
The way to do what you want is to write the following in src/lib.rs:
pub mod hotel;
This submodule named hotel would have to exist at either src/hotel.rs or src/hotel/mod.rs. Since we want it to have further submodules, by convention, the latter is used. So create a file named src/hotel/mod.rs, with the following contents:
pub mod guest;
pub mod hotel_manager;
pub mod hotel;
This creates a proper module structure we want with hotel as a submodule of the root module, which contains further submodules named guest, hotel_manager and hotel.
Then, in the crate::hotel::hotel_manager module, instead of using mod statements, you should use the following statements, which import the modules that have been declared elsewhere into the current scope:
use crate::hotel::guest;
use crate::hotel::hotel;
Note that in Rust, you can import items smaller than just the whole module. For example, if you have a struct named Hotel in the crate::hotel::hotel module, you can import only that with a statement like use crate::hotel::hotel::Hotel;

Correct place for private module to be used by multiple executables

Two executables' sources, foo.rs and bar.rs, are located in src/bin.
Private common functionality exists in src/bin/common.rs.
foo.rs and bar.rs include this functionality with:
mod common;
use common::{Bish, Bash, Bosh};
This works, but src/bin/common.rs doesn't feel like the right path for something which isn't going to be built into an executable.
Moving common.rs to src or src/lib stops foo.rs and bar.rs from seeing it.
Where should I put common.rs and how do I then import it?
A common approach to store shared parts at lib.rs and use these in binaries. Its usage, though, is a bit different than simply mod + use. In fact, library is a separate crate, so you need to access it via crate name (defined in Cargo.toml).
Cargo.toml:
[package]
name = "crate-name"
# ... the rest
src/bin/foo.rs:
fn main() {
crate_name::fun();
}
src/lib.rs:
pub fn fun() {}
example
├── Cargo.toml
└── src
├── bin
│   ├── bar.rs
│   └── foo.rs
├── common.rs
└── lib.rs
foo.rs and bar.rs:
#[path = "../common.rs"]
mod common;
use common::{Bish, Bash, Bosh};
also see: How can I use a module from outside the src folder in a binary project, such as for integration tests or benchmarks?

Importing non-root module from multiple non-root binaries

I am learning Rust and decided to write a simple client/server program. Both the client and the server will be using a very simple module I've already written. Knowing that this code might grow, I decided to compartmentalize my source for clarity. Right now my current hierarchy looks as follows:
├── Cargo.lock
├── Cargo.toml
├── README.md
├── src
│   ├── client
│   │   └── main.rs
│   ├── common
│   │   ├── communicate.rs
│   │   └── mod.rs
│   ├── lib.rs
│   └── server
│   └── main.rs
Many of the examples I found on Stack Overflow and the net provide great samples for when the main.rs is in the project root directory. Unfortunately I'm trying to do something different as shown above.
communicate.rs contains all of the network code I have written. Eventually I will add other Rust files here and include their public mod statement in mod.rs. Currently common/mod.rs all I have is
pub mod communicate;
Focusing on just the client folder, all I have is main.rs as shown. The file "header" lists
extern crate common;
use std::thread;
use std::time;
use std::net;
use std::mem;
use common::communicate;
pub fn main() {
// ...
}
Besides the fundamental [package] section, all I have in Cargo.toml is
[[bin]]
name = "server"
path = "src/server/main.rs"
[[bin]]
name = "client"
path = "src/client/main.rs"
When I try to build the client binary, the compiler complains that the common crate could not be found.
$ cargo build
Compiling clientserver v0.1.0 (file:///home/soplu/rust/RustClientServer)
client/main.rs:1:1: 1:21 error: can't find crate for `common` [E0463]
client/main.rs:1 extern crate common;
^~~~~~~~~~~~~~~~~~~~
error: aborting due to previous error
error: Could not compile `clientserver`.
To learn more, run the command again with --verbose.
I think this is because it is looking for a common crate within the client/ folder. I had this same problem when I tried the mod statement instead of extern crate statement.
use std::thread;
use std::time;
use std::net;
use std::mem;
mod common;
Gave me:
client/main.rs:6:5: 6:11 error: file not found for module `common`
client/main.rs:6 mod common;
^~~~~~
client/main.rs:6:5: 12:11 help: name the file either common.rs or common/mod.rs inside the directory "client"
I also tried (using the extern crate...) adding a lib.rs in the client whose contents are pub mod common; but I still get the same error as the first.
One potential solution I found to model it like this project, but this would require a Cargo.toml in every folder, something which I'd like to avoid.
I feel like I am close but am missing something.
You are not building common as a crate right now. The crates being built are the library clientserver (the default name for the library is the package name) and the binaries client and server.
Normally, extern crate clientserver; should work. However, if you want to name your library differently, you can do so by specifying a different name in a [lib] section in Cargo.toml. In this section, you can also specify a different source path for the library's main source file. In your case, it will probably be better, otherwise you'll end up with a crate named common and all of its contents would be in a module named common, so you'd have to access everything as common::common::foo. For example, by adding this to your Cargo.toml:
[lib]
name = "common"
path = "src/common/lib.rs"
you could combine src/lib.rs and src/common/mod.rs into src/common/lib.rs. Then, extern crate common; should work in your binaries.

How do I tell Cargo to build files other than main.rs?

Here is my directory structure:
lowks#lowkster ~/src/rustlang/gettingrusty $ tree .
.
├── Cargo.lock
├── Cargo.toml
├── foo.txt
├── src
│   ├── boolean_example.rs
│   ├── function_goodbye_world.rs
│   ├── listdir.rs
│   ├── looping.rs
│   ├── main.rs
│   ├── pattern_match.rs
│   └── write_to_file.rs
└── target
├── build
├── deps
├── examples
├── gettingrusty
└── native
6 directories, 11 files
When I run 'cargo build', it seems to only build main.rs. How should I change Cargo.toml to build the rest of the files too?
Put other.rs file into bin subfolder of src folder (./src/bin/other.rs). And run cargo build --bin other or cargo run --bin other
The Rust compiler compiles all the files at the same time to build a crate, which is either an executable or a library. To add files to your crate, add mod items to your crate root (here, main.rs) or to other modules:
mod boolean_example;
mod function_goodbye_world;
mod listdir;
mod looping;
mod pattern_match;
mod write_to_file;
To access items defined in another module from your crate root, you must qualify that item with the module name. For example, if you have a function named foo in module looping, you must refer to it as looping::foo.
You can also add use statements to import names in the module's scope. For example, if you add use looping::foo;, then you can just use foo to refer to looping::foo.
For more information, see Separating Modules into Different Files in The Rust Programming Language.
There are a few different types of binaries or targets that cargo recognizes:
binaries
libraries
benchmarks
integration tests
examples
For example, if the file boolean_example.rs is a standalone example that you want to run you can put in inside an examples directory and tell cargo about it like so:
[[example]]
name = "boolean" # examples/boolean.rs
This lets you invoke your example with cargo run --example boolean
Read the cargo book's page on package layout as well to see how these target directories can be structured.
you can include your testing in the main.rs file as following >>
Filename: src/main.rs
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn this_test_will_pass() {
let value = 4;
assert_eq!(4, value);
}
#[test]
fn this_test_will_fail() {
let value = 8;
assert_eq!(5, value);
}
}
Or call them from your tests file.
then run them using test command: cargo test
from filename: lib/tests.rs
mod tests;
tests::run();
in this case main.rs will be built but only tests.rs file will be
executed.
more prove:

Resources