This is the code (I'm using clap 3.2.20):
pub fn main() {
let m = Command::new("foo")
.arg(
Arg::new("bar")
.long("bar")
.required(false)
.action(ArgAction::SetTrue)
)
.get_matches();
if m.contains_id("bar") {
println!("Boom!");
}
}
It prints Boom! either I provide --bar option or not. Why and how to fix?
Per the documentation of contains_id
NOTE: This will always return true if default_value has been set. ArgMatches::value_source can be used to check if a value is present at runtime.
Now you might object that you're not using default_value, however you're using ArgAction::SetTrue:
When encountered, act as if "true" was encountered on the command-line
If no default_value is set, it will be false.
Emphasis mine.
So ArgAction::SetTrue implies default_value=false.
Therefore bar will always have a value, there's no need to check whether it does. Just fetch the value.
Related
I wrote this snippet:
let my_option: Option<()> = None;
if my_option.is_some() {
my_option.unwrap();
}
Clippy is telling me to use:
if let Some(..) = my_option {
my_option.unwrap();
}
What does Some(..) mean? Is it just dropping the value similar to how using _ would in some cases? And where can I find more information about when and where I can use the ..?
I think the Clippy warning is slightly misleading. Let's first look at the full warning message:
warning: called `unwrap` on `my_option` after checking its variant with `is_some`
--> src/main.rs:4:9
|
3 | if my_option.is_some() {
| ---------------------- help: try: `if let Some(..) = my_option`
4 | my_option.unwrap();
| ^^^^^^^^^^^^^^^^^^
|
= note: `#[warn(clippy::unnecessary_unwrap)]` on by default
= help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#unnecessary_unwrap
Clippy is complaining about the use of unwrap() after checking with is_some(). If you use is_some() only or unwrap() only, Clippy won't complain. It's only unwrapping after checking that's considered unidiomatic, since it can be easier done using if let.
I believe the reason for Clippy to suggest if let Some(..) = my_option is that the code does not track how the inner value is used later, so the suggestion needs to be somehow generic. In practice, you would hardly ever literally write .. here, but instead write something like
if let Some(value) = my_option
to bind the inner value to a variable. Also note that if let Some(..) = my_option does not consume the option, while if let Some(value) = my_option does (if the inner type is not Copy).
In summary:
If you want to check whether an option is Some, but are not interested in the contained value, you should use if my_option.is_some().
If you are certain at compile time that your option must be Some, and you want to extract the inner value, use my_option.unwrap() without any enclosing if.
If you don't know whether the option is Some, and you want to extract the inner value only in case it is, use if let Some(value) = my_value.
Completing the excellent answer by #SvenMarnach, .. does have a meaning in patterns and it's indeed very similar to _: while _ means "ignore one field here", .. means "ignore zero or more fields here".
As such, in almost any place you can use _ you can also use .., although it can lead to mistakes if fields are added later. The only exceptions are identifier patterns - you can do if let _ = 123 but not if let .. = 123, and struct patterns because it has special syntax: ignoring one field is Struct { field: _ }, while ignoring many is Struct { .. }.
Context: I am learning Rust & WebAssembly and as a practice exercise I have a project that paints stuff in HTML Canvas from Rust code. I want to get the query string from the web request and from there the code can decide which drawing function to call.
I wrote this function to just return the query string with the leading ? removed:
fn decode_request(window: web_sys::Window) -> std::string::String {
let document = window.document().expect("no global window exist");
let location = document.location().expect("no location exists");
let raw_search = location.search().expect("no search exists");
let search_str = raw_search.trim_start_matches("?");
format!("{}", search_str)
}
It does work, but it seems amazingly verbose given how much simpler it would be in some of the other languages I have used.
Is there an easier way to do this? Or is the verbosity just the price you pay for safety in Rust and I should just get used to it?
Edit per answer from #IInspectable:
I tried the chaining approach and I get an error of:
temporary value dropped while borrowed
creates a temporary which is freed while still in use
note: consider using a `let` binding to create a longer lived value rustc(E0716)
It would be nice to understand that better; I am still getting the niceties of ownership through my head. Is now:
fn decode_request(window: Window) -> std::string::String {
let location = window.location();
let search_str = location.search().expect("no search exists");
let search_str = search_str.trim_start_matches('?');
search_str.to_owned()
}
which is certainly an improvement.
This question is really about API design rather than its effects on the implementation. The implementation turned out to be fairly verbose mostly due to the contract chosen: Either produce a value, or die. There's nothing inherently wrong with this contract. A client calling into this function will never observe invalid data, so this is perfectly safe.
This may not be the best option for library code, though. Library code usually lacks context, and cannot make a good call on whether any given error condition is fatal or not. That's a question client code is in a far better position to answer.
Before moving on to explore alternatives, let's rewrite the original code in a more compact fashion, by chaining the calls together, without explicitly assigning each result to a variable:
fn decode_request(window: web_sys::Window) -> std::string::String {
window
.location()
.search().expect("no search exists")
.trim_start_matches('?')
.to_owned()
}
I'm not familiar with the web_sys crate, so there is a bit of guesswork involved. Namely, the assumption, that window.location() returns the same value as the document()'s location(). Apart from chaining calls, the code presented employs two more changes:
trim_start_matches() is passed a character literal in place of a string literal. This produces optimal code without relying on the compiler's optimizer to figure out, that a string of length 1 is attempting to search for a single character.
The return value is constructed by calling to_owned(). The format! macro adds overhead, and eventually calls to_string(). While that would exhibit the same behavior in this case, using the semantically more accurate to_owned() function helps you catch errors at compile time (e.g. if you accidentally returned 42.to_string()).
Alternatives
A more natural way to implement this function is to have it return either a value representing the query string, or no value at all. Rust provides the Option type to conveniently model this:
fn decode_request(window: web_sys::Window) -> Option<String> {
match window
.location()
.search() {
Ok(s) => Some(s.trim_start_matches('?').to_owned()),
_ => None,
}
}
This allows a client of the function to make decisions, depending on whether the function returns Some(s) or None. This maps all error conditions into a None value.
If it is desirable to convey the reason for failure back to the caller, the decode_request function can choose to return a Result value instead, e.g. Result<String, wasm_bindgen::JsValue>. In doing so, an implementation can take advantage of the ? operator, to propagate errors to the caller in a compact way:
fn decode_request(window: web_sys::Window) -> Result<String, wasm_bindgen::JsValue> {
Ok(window
.location()
.search()?
.trim_start_matches('?')
.to_owned())
}
This extremely simple Rust program:
fn main() {
let c = "hello";
println!(c);
}
throws the following compile-time error:
error: expected a literal
--> src/main.rs:3:14
|
3 | println!(c);
| ^
In previous versions of Rust, the error said:
error: format argument must be a string literal.
println!(c);
^
Replacing the program with:
fn main() {
println!("Hello");
}
Works fine.
The meaning of this error isn't clear to me and a Google search hasn't really shed light on it. Why does passing c to the println! macro cause a compile time error? This seems like quite unusual behaviour.
This should work:
fn main() {
let c = "hello";
println!("{}", c);
}
The string "{}" is a template where {} will be replaced by the next argument passed to println!.
TL;DR If you don't care why and just want to fix it, see the sibling answer.
The reason that
fn main() {
let c = "hello";
println!(c);
}
Cannot work is because the println! macro looks at the string at compile time and verifies that the arguments and argument specifiers match in amount and type (this is a very good thing!). At this point in time, during macro evaluation, it's not possible to tell that c came from a literal or a function or what have you.
Here's an example of what the macro expands out to:
let c = "hello";
match (&c,) {
(__arg0,) => {
#[inline]
#[allow(dead_code)]
static __STATIC_FMTSTR: &'static [&'static str] = &[""];
::std::io::stdio::println_args(&::std::fmt::Arguments::new(
__STATIC_FMTSTR,
&[::std::fmt::argument(::std::fmt::Show::fmt, __arg0)]
))
}
};
I don't think that it's actually impossible for the compiler to figure this out, but it would probably take a lot of work with potentially little gain. Macros operate on portions of the AST and the AST only has type information. To work in this case, the AST would have to include the source of the identifier and enough information to determine it's acceptable to be used as a format string. In addition, it might interact poorly with type inference - you'd want to know the type before it's been picked yet!
The error message asks for a "string literal". What does the word "literal" mean? asks about what that means, which links to the Wikipedia entry:
a literal is a notation for representing a fixed value in source code
"foo" is a string literal, 8 is a numeric literal. let s = "foo" is a statement that assigns the value of a string literal to an identifier (variable). println!(s) is a statement that provides an identifier to the macro.
If you really want to define the first argument of println! in one place, I found a way to do it. You can use a macro:
macro_rules! hello {() => ("hello")};
println!(hello!());
Doesn't look too useful here, but I wanted to use the same formatting in a few places, and in this case the method was very helpful:
macro_rules! cell_format {() => ("{:<10}")}; // Pads with spaces on right
// to fill up 10 characters
println!(cell_format!(), "Foo");
println!(cell_format!(), 456);
The macro saved me from having to duplicate the formatting option in my code.
You could also, obviously, make the macro more fancy and take arguments if necessary to print different things with different arguments.
If your format string will be reused only a moderate number of times, and only some variable data will be changed, then a small function may be a better option than a macro:
fn pr(x: &str) {
println!("Some stuff that will always repeat, something variable: {}", x);
}
pr("I am the variable data");
Outputs
Some stuff that will always repeat, something variable: I am the variable data
I have a TOML document in which some keys may or may not exist. E.g. this document is a valid document:
foo = "bar"
But also this is valid:
foo = "bar"
something = "else"
I am now trying to parse this document in Rust with the library toml-rs. However I could not find any guidance in the documentation on how to find out whether a key actually exists in my TOML document. Whenever I try to access this key the program panics with the error index not found.
I came up with the following minimalistic example that shows that the Rust code immediately fails when I try to access a key that does not exist:
use toml::Value;
fn main() {
let value = "foo = 'bar'".parse::<Value>().unwrap();
println!("{:?}", value["foo"]);
println!("{}", "before");
println!("{:?}", value["foo2"]);
println!("{}", "after");
}
Which leads to the output:
String("bar")
before
thread 'main' panicked at 'index not found', src/libcore/option.rs:1034:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
In the actual code of the library I found a comment for a function called get that states:
Also returns None if the given key does not exist in the map or the given index is not within the bounds of the array.
However, I am not sure whether this should also apply to my key access. At least to my tests it does not. I was also unable to find a function in the code that would check whether a key exists.
I guess there must be some way to find out whether a key exists in the TOML document?
get() and what you are doing are two completely different paths into the library. This by-key access is an implementation of Index<_> and will panic if the key does not exist.
This is what you are seeing in your code.
The real way to do what you are trying to do is indeed by using get(), which will return an Option, but first, we're going to sort out the case where the toml you feed isn't a table, like so:
use toml::{Value};
use toml::map::Map;
fn main() {
let value = "foo = 'bar'".parse::<Value>().ok().and_then(|r| match r {
Value::Table(table) => Some(table),
_ => None
}).unwrap_or(Map::new()); // This now contains a HashMap<String, Value>
println!("{:?}", value.get("foo"));
println!("{}", "before");
println!("{:?}", value.get("foo2"));
println!("{}", "after");
}
I'm fairly new to Rust so I came across this piece of code in the official Guide
let input = io::stdin().read_line()
.ok()
.expect("Failed to read line");
let input_num: Option<uint> = from_str(input.as_slice());
let num = match input_num {
Some(num) => num,
None => {
println!("Please input a number!");
return;
}
};
While the understand the first two statements (on input and inputnum), I'm not quite sure about the match statement. So I checked the documentation which shows that Option<T> can take two values , either None or Some(T) for some (object?) T. So I tested the following code:
io::println(
match input_num {
Some(num) => "somenum",
None => {
println!("Please input a number only!");
return;
}
}
);
This code works as expected; it prints somenum if you enter a number and otherwise it prints the error message. However, the compiler gives a warning stating: warning: unused variable:num, #[warn(unused_variable)] on by default. This confirmed my suspicions that the num inside the `match is used as a variable.
Question: How is it possible that rust does not complain about (in the Guide's example) having two variables with the same name num? Or does it "hand over" the pointer to the inside num to the outside num?
Also in the case of an empty return what exactly is returned? I'm guessing it is unit () because it is mentioned here that
functions without a -> ... implicitly have return type ()
Edit: Sorry for missing the obvious point. The return directly exits the function without bothering to put anything into num.
P.S. I noticed that using cargo build to compile does not give warnings the second time around (not making any changes). Does cargo keep tracking of versions or something?
Do you have any experiences with Java or C#? Many mainstream programing languages allows you to shadow a variable with another variable with the same name in a new block scope. For example, take a look at this C# code:
using System;
public class Test
{
public static void Main()
{
int x = 10;
{
int x = 20;
Console.WriteLine(x);
}
Console.WriteLine(x);
}
}
http://ideone.com/OuMlc8
In fact Rust is more flexible in terms of shadowing: you can shadow variables as many times as you want without explicitly making a brace block.
fn main() {
let i = 10i32;
let i = "foo";
println!("{}", i);
}
http://is.gd/CUHUOL
Here, the first i and the second i have no relationships. We just lose a way to use (refer to) the first i.
This confirmed my suspicions that the num inside the `match is used as a variable.
Yes, pattern matching can introduce new variables. But the new variable num introduced by pattern matching has no direct relationship with the outer num whose value is being given by the match expression.
I can translate your Rust code into less idiomatic Rust to illustrate what was happening:
let num =
if input_num.is_some() {
let num = input_num.unwrap();
num
} else {
println!("Please input a number!");
return;
}
;
As for not observing warnings from cargo build, I suspect it was because Cargo had already built .o from your code and didn't bother to repeat the same job twice, knowing that there was no change in your code.