I have a function file_to_bytes() in lib.rs which I need to call from both lib.rs and runtime.rs (and tests.rs, but it's OK from there).
I also have a file klass_parser.rs that contains a struct OtKlassParser that needs to be used in both lib.rs and runtime.rs.
I can't seem to arrange the mod and use declarations correctly - what's the canonical way to do this, and what section of the documentation covers this case?
Answering my own question as this is not covered in the docs.
There appears to be no easy way to do this directly. Instead, the dependency graph that is described in the question is an indication that actually the project needs more structure and to be composed of multiple crates.
In this case, a workable structure looks something like the following:
klass_parser.rs should be its own crate
Majority of lib.rs should move into a new crate (called something like vm)
runtime.rs should become a third separate crate
Remainder of lib.rs should move into main.rs
lib.rs should disappear completely
Related
I like naming my folders and files by the kebab-case convention.
Recently, I've been learning rust and learned a bit about modules. In one of the examples, I have a file named: distinct-powers.rs. Whenever I do mod distinct-powers to put the code in scope, I obviously get a syntax error since Rust cannot handle kebab-case. The error is: Syntax Error: expected BANGrust-analyzer which I don't think is informing us of anything since it thinks the error is completely different.
Is there any way to circumvent this limitation of Rust?
If you really want to, you can place modules’ code at arbitrary filenames:
#[path = "distinct-powers.rs"]
mod distinct_powers;
But please don’t — one of the great things about Rust is that there is a single standard for project layout, which makes it easy to dive into other people’s code. Every customization is a disruption to someone finding the code they’re looking for.
There are a couple of locations where hyphens are allowed, that is, a kebab-case name can be used in a Rust package. They all have to do with Cargo, the build system, rather than the Rust language itself.
The name of the package itself (as specified in Cargo.toml) can be kebab-case. If the package is a library, Cargo will automatically translate this to snake_case for the library's crate name (which is how you refer to it within Rust code in dependents). This is quite commonly done.
For example, in this docs URL, https://docs.rs/ordered-float/latest/ordered_float/, you can see that the package name is ordered-float and the crate name (which could be overridden but was not) is derived from that as ordered_float.
The name of a binary, example, or test target can contain hyphens. This means that the binaries built in target/ can have kebab-case names — cargo run --bin distinct-powers is valid.
These names can all be used by Cargo target auto-discovery and thus are not considered non-standard layout.
kebab-enthusiast/
Cargo.toml
src/
lib.rs
snake_module.rs
bin/
kebab-binary.rs
examples/
kebab-example.rs
tests/
kebab-test.rs
How do you get rust to recognize a module within a crate? I believed it was enough to declare mod [module name] in lib.rs. Is it not?
Error:
error[E0432]: unresolved import `crate::a`
--> src/bin/b.rs:2:12
|
2 | use crate::a::f;
| ^ could not find `a` in the crate root
src/a.rs:
pub fn f() {}
src/bin/b.rs:
use crate::a::f;
fn main() {}
src/lib.rs:
mod a;
Cargo.toml:
[package]
name = "m"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[[bin]]
name = "b"
path = "src/bin/b.rs"
[dependencies]
You are confusing crates and packages. As per the corresponding chapter in the book:
A crate is the smallest amount of code that the Rust compiler considers at a time. [...] A crate can come in one of two forms: a binary crate or a library crate.
This means that your lib.rs and bin/b.rs files define two separate crates and thus crate refers to different things. Both belong to the same package which is defined by your Cargo.toml file. To use functions from your library crate in your binary crate, use the crate name instead of crate. In your case, the crate name is the same as the package name, which is m. Note that you will have to mark your library items as pub to use them in another crate.
An example of this can be found in the bat program, which uses both crate and bat in the imports of the bat binary crate:
https://github.com/sharkdp/bat/blob/2dbc88d3afdacf6449f299c70f29e5456904779b/src/bin/bat/main.rs
When a binary is compiled it is seen as the root of the project structure. So lib.rs is not compiled for binary targets and you can not use any of its modules unless you also add them in your binary. The most common approach is to put your binaries in the src folder with lib.rs so you don't need to worry about the folder structure when using your modules. I suppose you could use include!("lib.rs"); at the top of your binary to make it inherit everything from your library, but I have not tried it and it would likely be considered bad form.
However, you can sidestep the issue by making your binary depend on your library. The easiest way to do this is by making your binary its own crate which depends on your library.
There are also some ways to get around this, is by making a crate a dependency of itself, but these solutions always make me feel a little uneasy about the stability of the solution. This answer can give you a general idea of what that looks like.
Here are some examples various solutions. In case you are wondering, these are just the first crates I found that fit the design pattern. I do not know what some of them do/contain.
https://github.com/stanislav-tkach/os_info (Workspace members)
https://github.com/sagiegurari/cargo-make (Binaires in source)
https://github.com/hominee/dyer (Subcrates (dyer-macros))
https://github.com/clap-rs/clap (Example binaries)
Can I use a module that is located inside the directory that is located next to the executable file?
--src
|
|--main.rs
|
|--dir
| |--my_file.rs
I don't really want to start a new crate, but I can't write something like:
mod my_file;
or
mod dir::my_file;
If you know Python, Rust's modules are somewhat similar to Python's packages in that the directories are not intrinsically meaningful, a "signal" file is necessary to make them so.
For historical reaons, in rust there are two options for that signal files:
a mod.rs file nested inside the directory
or a dir.rs file next to the directory
Both work and their behaviour is equivalent, so it mostly comes down to how you come at it. mod.rs has the advantage that it makes the entire module into a unit you can move around, while dir.rs provides a clearer descriptor and makes it easier to start with a single file then split things out later on.
So in the above what you need is to add a dir.rs or dir/mod.rs which contains:
mod my_file;
and main.rs should contain the stanza
mod dir;
and things should work out, dir and dir::my_file now become available (in sibling modules you may need to use them).
In addition, it is not clear to me if I create a crate, I should always take out all open methods in lib.rs ?
I don't understand what you're talking about, what is an "open method"?
[...] what is the advantage of crates in general
In Rust a crate is a unit of:
visibility, a crate is a single thing which impacts visibility (pub(crate) and a few other things) and orphan rules (you can only implement a trait on a type if either was defined in the current crate), a crate can also have circular dependencies internally (not that it's necessarily a good thing though it's often convenient) while intra-crate dependencies must be a DAG
code distribution, a crate is a unit you can upload to cargo (or an equivalent private repository) and depend on
code generation, different crates can always be built concurrently (as long as they don't depend on one another, obviously), intra-crate concurrency is a lot less reliable
I have a rust project with main.rs, some other modules in the main.rs level and several submodules underneath. I use the anyhow crate in almost all of them.
Is there a clean way to declare use anyhow; only once (probably in main.rs?) without the need to redclare its usage in every single mod/file?
Note that anyhow itself should be accessible without use (as of Edition 2018), because this is a crate name. If you mean to import something within that crate for your entire project (use anyhow::Context;), then no, there is no standard¹ way to have that.
Each module has its own scope for such symbols, which are imported via use. The one exception is in the standard preludes, defined by the compiler itself. These are what enable you to use Copy, Result, and others without having to import them explicitly. However, this is naturally out of your control.
There is also a common pattern for some libraries to provide their own prelude: a module that re-exports symbols that are likely to be useful. anyhow does not provide one at the time of writing.
See also:
What is the prelude?
¹Almost anything becomes possible with macros or another preprocessor, but it is hardly a good tradeoff for this case.
Rust doesn't support global imports.
However, for global macro imports, there is a way, as explained here. That won't solve your problem completely, but it will at least allow you to avoid importing the macros.
In your main.rs you can add this:
#[macro_use]
extern crate anyhow;
And then you can use macros like bail! and ensure! all around your project, without the need to explicitly import them.
I think that use is used to import identifiers into the current scope and extern crate is used to declare an external module. But this understanding (maybe wrong) doesn't make any sense to me. Can someone explain why Rust has these two concepts and what are the suitable cases to use them?
extern crate foo indicates that you want to link against an external library and brings the top-level crate name into scope (equivalent to use foo). As of Rust 2018, in most cases you won't need to use extern crate anymore because Cargo informs the compiler about what crates are present. (There are one or two exceptions)
use bar is a shorthand for referencing fully-qualified symbols.
Theoretically, the language doesn't need use — you could always just fully-qualify the names, but typing std::collections::HashMap.new(...) would get very tedious! Instead, you can just type use std::collections::HashMap once and then HashMap will refer to that.
Since Rust 2018 you're only required to add external dependencies to your Cargo.toml, so you no longer need to use extern crate foo.
use works the same as before.
Read more in the official documentation.