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.)
Related
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.
I am New to Rust and come from another programming language.
here are my dependencies
[dependencies]
piston_window = "0.123.0"
piston = "0.53.1"
gfx = "0.18.2"
I am having a problem with the manual type hinting
when I load a Texture without adding a type hint the texture is loaded and can be displayed in the window.
let dirt = piston_window::Texture::from_path(&mut window.create_texture_context(),
Path::new("assets/sprites/dirt.png"), Flip::None, &TextureSettings::new()).unwrap();
when I try to manually add the type hint which vscode recommends me, I get an Error.
e.g
let dirt: Texture<Resources>= piston_window::Texture::from_path(&mut window....
The Error can be reproduced by running the following main file in an environment with a dummy.png file
extern crate piston_window;
extern crate piston;
extern crate gfx;
use piston_window::{
PistonWindow,
WindowSettings,
Flip,
TextureSettings,
Texture,
};
use std::path::Path;
fn main(){
let opengl_version = piston_window::OpenGL::V3_2;
let mut window: PistonWindow = WindowSettings::new("test", [1280., 720.]).exit_on_esc(true).graphics_api(opengl_version).build().unwrap();
let working = piston_window::Texture::from_path(&mut window.create_texture_context(), Path::new("dummy.png"), Flip::None, &TextureSettings::new()).unwrap();
let failing:Texture<gfx::Resources> = piston_window::Texture::from_path(&mut window.create_texture_context(), Path::new("dummy.png"), Flip::None, &TextureSettings::new()).unwrap();
}
The actual type is Texture<gfx_device_gl::Resources>, not Texture<gfx::Resources>.
Note that gfx::Resources is a trait, not a type. In Texture<R> the R generic type must not be covariant, and even if it were the type would likely need to be Texture<dyn gfx::Resources>. While it's true that gfx_device_gl::Resources implements gfx::Resources, that doesn't on its own imply that Texture<gfx_device_gl::Resources> and Texture<dyn gfx::Resources> are compatible types. (The R type parameter would also need to be covariant.)
If you want to ensure that failing is a Texture without giving a particular resource type, you could use the annotation Texture<_> instead, which permits the compiler to infer Texture's generic type argument.
Further reading:
The Rustonomicon - Subtyping and Variance
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 am using rust's clap to investigate command line arguments. is it possible to specify which one will be input as a command line argument in clap?
Is it possible to specify the type like type=int in the following python code?
parser = argparse.ArgumentParser()
parser.add_argument("--some_string", type=str, required=True, )
What I am looking for is to specify the type of the command line arguments at the stage of defining them, as in the above python code.
I would like to use clap to do the following
./target/release/~~ --i32_args 12 --f32_args -32.5
Thus, if the type and variable match, we want it to work without error.
./target/release/~~ --i32_args -32 --f32_args 1
In this case, I want to cause an immediate panic and terminate the operation.
What I am looking for is something that does not use methods such as value_of, but judges the type of the command line argument at the time the command is executed, and executes it if the type is correct, or panic immediately if the type is wrong.
It is my understanding that in value_of_t, the type is not determined until the data is retrieved. Therefore, I think it is different from what I am looking for.
A bit late to the party, but you can use value_parser with Clap v4
use clap::{arg, value_parser, Command}; // Clap v4
use std::net::Ipv4Addr;
fn main() {
let matches = Command::new("clap-test")
.arg(
arg!(--ip <VALUE>)
.default_value("127.0.0.1")
.value_parser(value_parser!(Ipv4Addr)),
)
.arg(
arg!(--port <VALUE>)
.required(true)
.value_parser(value_parser!(u16)),
)
.arg(arg!(--f32 <VALUE>).value_parser(value_parser!(f32)))
.get_matches();
println!(
"ip {:?}, port {:?}, float {:?}",
matches.get_one::<Ipv4Addr>("ip").expect("required"),
matches.get_one::<u16>("port").expect("required"),
matches.get_one::<f32>("f32"),
);
}
Here's what I'm trying to do: open all the command line arguments as (binary) files and read bytes from them. The constantly changing syntax here is not conductive to googling, but here's what I've figured out so far:
use std::io::{File, result};
use std::path::Path;
use std::os;
fn main() {
let args = os::args();
let mut iter = args.iter().skip(1); // skip the program name
for file_name in iter {
println(*file_name);
let path = &Path::new(*file_name);
let file = File::open(path);
}
}
Here's the issue:
test.rs:44:31: 44:41 error: cannot move out of dereference of & pointer
test.rs:44 let path = &Path::new(*file_name);
I've hit a brick wall here because while I'm fine with pointers in C, my understanding of the different pointer types in rust is practically non-existent. What can I do here?
Try &Path::new(file_name.as_slice())
Unfortunately, due to the trait argument that Path::new() takes, if you pass it a ~str or ~[u8] it will try and consume that type directly. And that's what you're passing with *file_name. Except you can't move out of a pointer dereference in Rust, which is why you're getting the error.
By using file_name.as_slice() instead (which is equivalent, in this case, to (*file_name).as_slice(), but Rust will do the dereference for you) it will convert the ~str to a &str, which can then be passed to Path::new() without a problem.