I decided that I want to learn Rust by doing the Advent of Code challenges one day at a time. I was thinking that maybe a good way to structure my project would be something like this:
-aoc:
-----
|
|-src:
|-y2021:
|-y2022:
|-d1:
|-d2:
|-src:
|-main.rs
|-target:
|-mod.rs
|-Cargo.toml
|-mod.rs
|-main.rs
|-target:
|-Cargo.toml
The contents of the files so far are as follows.
Inside aoc/Cargo.toml:
[package]
name = "aoc"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
anyhow = "1.0.66"
criterion = "0.4.0"
futures = "0.3.25"
itertools = "0.10.5"
lazy_static = "1.4.0"
serde = "1.0.130"
Inside aoc/src/main.rs:
pub mod y2022;
fn main() {
println!("Hello, Advent of Code!");
}
Inside aoc/src/y2022/mod.rs:
pub mod d1;
Inside aoc/src/y2022/d1/mod.rs:
???
Inside aoc/src/y2022/d1/src/main.rs:
fn simple(i: &[u8]) -> usize {
...
}
If this way of organizing this project in Rust makes sense, can someone correct the above file. Specifically what should be included where, and additionally how should I test the various days, or if I want to, how can I execute the tests for all the days, or all the years respectively. Should there be a test file somewhere, how do I call these test files, how to I aggregate the tests by day and/or year.
If on the other hand this is not how things should be done in Rust, can you suggest a better alternative of structuring this project?
I would use a different binary package for each day, and make aoc a workspace.
Related
I'm trying to read command line args using clap, then use the parsed results to drive a backened whose progress is displayed by a gtk App (just using the app as the easiest way to display a changing graphical window, maybe there's a better way.)
MRE:
main.rs:
use clap::Parser;
use gtk::{prelude::*, Application};
#[derive(Parser, Debug)]
//#[command(author, version, about, long_about = None)]
struct Example {
#[arg(long)]
words: String,
number: Option<usize>,
}
fn main() {
let config = Example::parse();
println!("{:?}", config);
let app = Application::builder()
.application_id("org.scratch.Scratch")
.build();
gtk::init().expect("GTK init failed");
app.run();
}
Cargo.toml:
[package]
name = "scratch"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
gtk = "*"
clap = { version = "4.1.4", features = ["derive"] }
and then do:
cargo run --release -- --words Red
When I do this, I get:
Example { words: "Red", number: None }
Unknown option --words
If I comment out the app.run(); call, the program runs as expected.
To me this says that clap is reading the arguments but leaving them in place, and then GTK is panicking because it doesn't know what to do with them. Right? Presumably I either need to tell GTK not to look at the command line or tell clap (or rust) to flush it. How do I do either?
Bonus question: This isn't the only issue I'm having with GTK. What other options do I have? I just need a draw surface I can draw colored rectangles (or better... pixels?) on a loop.
Found an answer at setup rust + gtk::Application to ignore --config argument : Just do the run call like:
let empty: Vec<String> = vec![];
app.run_with_args(&empty);
instead of app.run().
This explicitly passes no args to the GTK app, rather than passing through the actual command line args used to call your program.
(Maybe this should be closed as a duplicate, but it took me a bunch of time to figure out the MRE before I could find this, so I'm hoping maybe this question will be an easier signpost for the next guy. It was not at all clear when all I had was an "unknown option" error with no obvious source.)
What is the exact set of rules that rust uses to look up a module from a file?
Every explanation I have found online about modules says, "this is the purpose of modules, here is an example of one, ..." None give the complete, comprehensive, 100% accurate explanation for how rust looks up modules. Even the rust reference doesn't tell you whether both the crate root and the importing file need to declare mod! There is no simple ruleset I can use to tell whether it will work.
I'm looking for something I can follow, like:
Rust looks at the name, parsing :: like subdir::subdir::name
Rust looks to see if there is a file name.rs in the same directory and name/mod.rs
There is not allowed to be both a name.rs and a name/mod.rs.
Then, Rust...???
This is best explained starting from inline modules. Modules are arranged into a hierarchy from the crate root. Every crate, after some desugaring, looks something like this:
// root
pub mod a {
pub mod b {
pub const X: u8 = 1;
}
}
mod foo {
}
Referring to an item in the tree is pretty simple:
:: goes "down" a level
super:: goes "up" a level
crate:: goes to the root level
Examples for referring to X:
a::b::X from the crate root
crate::a::b::X from anywhere in the crate
super::a::b::X from within module foo
b::X from within module a
mod a; is really just syntax sugar for either of the following:
#[path = "foo.rs"]
mod foo;
// or
#[path = "foo/mod.rs"]
mod foo;
Which further desugar to:
mod foo {
include!("foo.rs");
}
// or
mod foo {
include!("foo/mod.rs");
}
If foo.rs (or foo/mod.rs) contains a mod bar; then the whole tree would look like:
mod foo {
mod bar {
// contents of `bar.rs` (or `foo/bar/mod.rs`)
}
// remaining contents of `foo.rs`
}
Please note that the usage of mod.rs, while still supported, is discouraged. Instead, it's recommended to use foo.rs for crate::foo and place any submodules of foo in the foo/ directory.
crate:: just always corresponds to the root being compiled at the time. If your crate is sufficiently complex or doesn't follow convention, then certain crate::... item paths can refer to different things in different files. But confusion is easily avoidable by following conventions.
I'm new to Rust. I'm trying to create a static variable DATA of Vec<u8> in a library so that it is initialized after the compilation of the lib. I then include the lib in the main code hoping to use DATA directly without calling init_data() again. Here's what I've tried:
my_lib.rs:
use lazy_static::lazy_static;
pub fn init_data() -> Vec<u8> {
// some expensive calculations
}
lazy_static! {
pub static ref DATA: Vec<u8> = init_data(); // supposed to call init_data() only once during compilation
}
main.rs:
use my_lib::DATA;
call1(&DATA); // use DATA here without calling init_data()
call2(&DATA);
But it turned out that init_data() is still called in the main.rs. What's wrong with this code?
Update: as Ivan C pointed out, lazy_static is not run at compile time. So, what's the right choice for 'pre-loading' the data?
There are two problems here: the choice of type, and performing the allocation.
It is not possible to construct a Vec, a Box, or any other type that requires heap allocation at compile time, because the heap allocator and the heap do not yet exist at that point. Instead, you must use a reference type, which can point to data allocated in the binary rather than in the run-time heap, or an array without any reference (if the data is not too large).
Next, we need a way to perform the computation. Theoretically, the cleanest option is constant evaluation — straightforwardly executing parts of your code at compile time.
static DATA: &'static [u8] = {
// code goes here
};
However, in current stable Rust versions (1.58.1 as I'm writing this), constant evaluation is very limited, because you cannot do anything that looks like dropping a value, or use any function belonging to a trait. It can still do some things, mostly integer arithmetic or constructing other "almost literal" data; for example:
const N: usize = 10;
static FIRST_N_FIBONACCI: &'static [u32; N] = &{
let mut array = [0; N];
array[1] = 1;
let mut i = 2;
while i < array.len() {
array[i] = array[i - 1] + array[i - 2];
i += 1;
}
array
};
fn main() {
dbg!(FIRST_N_FIBONACCI);
}
If your computation cannot be expressed using const evaluation, then you will need to perform it another way:
Procedural macros are effectively compiler plugins, and they can perform arbitrary computation, but their output is generated Rust syntax. So, a procedural macro could produce an array literal with the precomputed data.
The main limitation of procedural macros is that they must be defined in dedicated crates (so if your project is one library crate, it would now be two instead).
Build scripts are ordinary Rust code which can compile or generate files used by the main compilation. They don't interact with the compiler, but are run by Cargo before compilation starts.
(Unlike const evaluation, both build scripts and proc macros can't use any of the types or constants defined within the crate being built itself; they can read the source code, but they run too early to use other items in the crate in their own code.)
In your case, because you want to precompute some [u8] data, I think the simplest approach would be to add a build script which writes the data to a file, after which your normal code can embed this data from the file using include_bytes!.
I am trying to build a single Rust binary executable. In the src directory I have four files:
main.rs:
use fasta_multiple_cmp::get_filenames;
fn main() {
get_filenames();
}
mod.rs:
pub mod fasta_multiple_cmp;
pub mod build_sequences_matrix;
fasta_multiple_cmp.rs:
pub mod fasta_multiple_cmp {
...
pub fn get_filenames() {
let args: Vec<String> = env::args().collect();
...
build_sequences_matrix.rs:
pub mod build_sequences_matrix {
use simple_matrix::Matrix;
...
Cargo told me:
src/main.rs:3:5
|
3 | use fasta_multiple_cmp::get_filenames;
| ^^^^^^^^^^^^^^^^^^ use of undeclared crate or module `fasta_multiple_cmp
I believed I understood some little things, but ther I'm lost. What is going on?
Thaks for any hint!
Your original code wasn't working because Rust's use is looking for a crate to import from, not just a file in the same directory.
If you want to separate logic into other files, you will need to break apart your project into both a binary and into a library (aka - a crate that your binary can use for imports).
This can be done in your Cargo.toml as:
[package]
edition = "2018"
name = "my_project"
version = "1.2.3"
... your other settings ...
[[bin]]
edition = "2018"
name = "whatever_name_for_binary"
path = "src/main.rs"
This means that your binary will be compiled into a file called "whatever_name_for_binary" in your target folder. It also means your non-binary files act as a library, and can be imported by your binary by using use my_project::some_module;
Because your project will now contain both a library and binary, you will need to specify when you want to run/compile the binary.
You do this by running: cargo run --bin whatever_name_for_binary
For more information, see the Rust Book's Chapter on Modules.
I created a new Cargo project: cargo new --lib hyphen-crate.
src/lib.rs
pub fn add(a: u32, b: u32) -> u32 {
a + b
}
tests/addition.rs
use hyphen_crate::add;
#[test]
fn addition_test() {
assert_eq!(5, add(2, 3));
}
Cargo.toml
[package]
name = "hyphen-crate"
version = "0.1.0"
authors = ["xolve"]
edition = "2018"
[dependencies]
I have searched and seen many discussions if hyphens should be allowed in names of crates or packages, but no link mentions a solution.
What I see is that the crate name hyphen-crate is automatically transformed to hyphen_crate and it compiles and tests successfully.
Hyphens are allowed in package names, but they are converted to underscores internally, because hyphens are not valid characters in Rust identifiers.
It seems that this automatic conversion was not always the case, and crates with hyphens had to be manually renamed in order to import them; eg. extern crate "hyphen-crate" as hyphen_crate;. See the Hyphens Considered Harmful RFC for some details on that.