How to build a Rust binary executable - rust

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.

Related

How to correctly setup a project with multiple folders

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.

How do I fix my compatability errors when using plotters::CairoBackend with GTK

I am trying to draw an image on a very basic GUI. I am using GTK for the GUI and want to use the plotters crate for the drawing but am having issues initiating the Backend that plotters uses.
I have successfully managed to use the BitMapBackend to draw to a file - there is no shortage of examples on the internet for this. However, I cannot for the life of me find an example of how to use the CairoBackend to draw onto a gtk DrawingArea.
My issue arises when I try to connect the draw event to the GTK DrawingArea. I use the connect_draw method which works as expected returning a context to pass to the CairoBackEnd. The problem is that the type of context returned by connect_draw is not compatible with the type of context required by the CairoBackend. I receive context of type 'gtk::cairo::Context' (defined in cairo-rs v0.16.7) but the backend requires a context of 'cairo::context:Context' (defined in cairo-rs v0.15.12). Clearly my dependencies are using different versions of the cairo-rs crate. Presumably, the versions of my dependencies are not compatible with each other. How can I fix this? How do I find out what version of each dependency will work with the others? My cargo.toml file contains:
[package]
name = "testing3"
version = "0.1.0"
edition = "2021"
[dependencies]
gtk = "0.16.2"
plotters = "0.3.4"
plotters-cairo = "0.3.2"
My rust code is:
use gtk::prelude::*;
use plotters::prelude::*;
use gtk::{Application, ApplicationWindow};
fn main() {
let app = Application::builder().build();
app.connect_activate(build_gui);
app.run();
}
fn build_gui(app: &Application){
let win = ApplicationWindow::builder().application(app).default_width(320).default_height(200).title("Test").build();
let da: gtk::DrawingArea = gtk::DrawingArea::builder().build();
da.connect_draw(move|_, c|{
let root = plotters_cairo::CairoBackend::new(c, (1024, 768)).unwrap().into_drawing_area();
gtk::Inhibit(false)
});
win.add(&da);
win.show_all();
}
my error message is:
--> src/main.rs:21:49
|
21 | let root = plotters_cairo::CairoBackend::new(c, (1024, 768)).unwrap().into_drawing_area();
| --------------------------------- ^ expected struct `cairo::context::Context`, found struct `gtk::cairo::Context`
| |
| arguments to this function are incorrect
|
= note: struct `gtk::cairo::Context` and struct `cairo::context::Context` have similar names, but are actually distinct types
note: struct `gtk::cairo::Context` is defined in crate `cairo`
--> /home/maindesktop/.cargo/registry/src/github.com-1ecc6299db9ec823/cairo-rs-0.16.7/src/context.rs:72:1
|
72 | pub struct Context(ptr::NonNull<cairo_t>);
| ^^^^^^^^^^^^^^^^^^
note: struct `cairo::context::Context` is defined in crate `cairo`
--> /home/maindesktop/.cargo/registry/src/github.com-1ecc6299db9ec823/cairo-rs-0.15.12/src/context.rs:72:1
|
72 | pub struct Context(ptr::NonNull<cairo_t>);
| ^^^^^^^^^^^^^^^^^^
= note: perhaps two different versions of crate `cairo` are being used?
note: associated function defined here
--> /home/maindesktop/.cargo/registry/src/github.com-1ecc6299db9ec823/plotters-cairo-0.3.2/src/backend.rs:70:12
|
70 | pub fn new(context: &'a CairoContext, (w, h): (u32, u32)) -> Result<Self, CairoError> {
| ^^^
For more information about this error, try `rustc --explain E0308`.
error: could not compile `testing3` due to previous error
------------------
(program exited with code: 101)
So with more digging around I have found the solution.
As I identified from the error message, my code had a compatibility problem. The gtk v0.16.2 dependency in my toml file depended itself on cairo-rs v0.16.7 where as the plotters-cairo v0.3.2 depended on cairo-rs v15.12. Clearly, I had to use an earlier version of the gtk dependency - one that depended on cairo-rs v15.12. But which one.
On the Doc.rs website I could find the gtk crate documents on https://docs.rs/gtk/0.6.0/gtk/. A tab on top left, gtk-0.6.0, on being selected provided both dependency data and versioning options. I could see that the latest version of the gtk crate did indeed depend on cairo-rs 0.6.0. Selecting the various versions I was able to find the latest version that still depended on cairo-rs v15.xx. In this case it was gtk v0.15.5. Once I replaced my gtk dependency with that version, all was good. So my new toml was:
[package]
name = "testing3"
version = "0.1.0"
edition = "2021"
[dependencies]
gtk = "0.15.5"
plotters = "0.3.4"
plotters-cairo = "0.3.2"
Now by adding the code:
c.rectangle(10.0, 10.0, 70.0, 70.0);
c.fill().expect("Drawing failed");
root.fill(&RED).unwrap();
above my gtk::Inhibit(false) statement, I could indeed create a red rectangle

Target binary file level generated documentation in Rust project

I have a Rust project consisting of two binary sources sharing the same library:
Cargo.toml:
<...>
[[bin]]
name = "quoter_xml"
path = "src/quoter_xml.rs"
[[bin]]
name = "quoter"
path = "src/quoter.rs"
<...>
src/quoter_xml.rs:
use <...>
fn main() {
<...>
}
src/quoter.rs:
use <...>
fn main() {
<...>
}
src/lib.rs:
pub mod <...>
When I compile it, I get two binaries: quoter_xml and quoter_xml.
When I generate doc using cargo doc, I get independent doc files doc/quoter_xml/index.html and doc/quoter/index.html.
What I want is to add some description texts to the top of these doc files.
I've tried to add "//!"-type comments to binary sources, for example:
src/quoter_xml.rs:
use <...>
//! quoter_xml - this binary is for <..>
fn main() {
<...>
}
But if I compile project after it, I get an error:
error[E0753]: expected outer doc comment
--> src/quoter_xml.rs:10:1
|
10 | //! quoter_xml - this binary is for <..>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: inner doc comments like this (starting with `//!` or `/*!`) can only appear before items
help: you might have meant to write a regular comment
|
10 - //! quoter_xml - this binary is for <..>
10 + // quoter_xml - this binary is for <..>
|
Compiler consider to use regular comments, but I don't want it: I need comments that will get to the top of doc pages, "//!"-type comments.
I've search Internet and found recommendation to place these type of comments in src/lib.rs file, but this is not what I want. I want to have independent top-level comments in doc for each binary file.
Any suggestion?
I believe you simply need to move your comment above the use statement in quoter_xml.rs.
//! quoter_xml - this binary is for <..>
use <...>
// Crate level doc comment cannot be after any code.
fn main() {
<...>
}

How do I specify that Cargo should build binaries only on a certain target?

I have a Cargo project set up with a library and a binary. The library is meant to be used on many platforms, including Android, while the binary is only meant to be used on Linux. As such, the binary contains a bunch of Linux-specific code that does not compile when I target Android. Is there a way to specify (without using features) that the binary should only be compiled on Linux?
I tried putting #![cfg(target_os = "linux")] in the main.rs of my binary, but then I got this error:
error[E0601]: `main` function not found in crate `server`
--> src/bin/server/main.rs:1:1
|
1 | / #![cfg(target_os = "linux")]
2 | |
3 | | use anyhow::{self, Context};
4 | |
... |
36 | | }
37 | | }
| |_^ consider adding a `main` function to `src/bin/server/main.rs`
I had exactly the same problem. The official Rust docs do not seem to mention how to solve it. But I found a solution.
The trick is to specify two main() functions in the file main.rs. The first one contains the code you want to execute when you are on Linux. The second one remains empty. With the help of conditional compilation, you tell the compiler which of the two main() functions to compile.
// within main.rs
#[cfg(target_os = "linux")]
fn main() {
// Your Linux-specific code goes here...
}
#[cfg(not(target_os = "linux"))]
fn main() {} // remains empty for all other OS
If you have OS-specific imports or other code at the top-level, just wrap it inside a module and use conditional compilation for it as well.
#[cfg(target_os = "linux")]
mod linux {
use anyhow::{self, Context};
// ...
}
The below link has related information that should help you in terms of target selection and selective compilation https://doc.rust-lang.org/cargo/commands/cargo-build.html#target-selection

Why is changing hyphenated crate names to underscored names possible and what are the rules for naming under such ambiguous scenarios?

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.

Resources