ArgMatches' get_one cannot downcast f64 - rust

I have used clap crate to parsing arguments in my code.
Minimal structure of my code about defining and parsing arguments are as follow.
use clap::builder::Command;
use clap::{Arg, ArgMatches};
let matches = Command::new("test")
.arg(Arg::new("mass")
.short('m')
.takes_value(true))
.get_matches()
let mass: f64 = *matches.get_one::<f64>("mass").unwrap();
But I face an error "thread 'main' panicked at 'Mismatch between definition and access of mass. Could not downcast to f64, need to downcast to alloc::string::String"
I can fix it by using parse() from String to f64.
let mass: f64 = *matches.get_one::<String>("mass").unwrap().parse().unwrap();
I want to know that why only f64 cannot be parsed by get_one function differ from the case of boolean or usize.

I found why the panic occurs, and how to prevent panic.
The panic occurs because clap does not detect floating point type argument automatically.
We should specify the value parser (or equivalently the type of argument) to clap (at the time we define command) for floating point or other custom types.
let matches = Command::new("test")
.arg(Arg::new("mass")
.short('m')
.takes_value(true))
.value_parser(clap::value_parser!(f64))
.get_matches()
Then, the panic is resolved.
In the above code block, the result of clap::value_parser!(f64) is _AnonymousValueParser(ValueParser::other(f64))

In my case, it was simply failing to parse the value as the provided type (and displaying a not-very-helpful error message). The type I was trying to parse it as was &str:
matches.get_one::<&str>("arg");
This was making the programme panic, as it fails to parse the input as &str. The solution I used was to parse as String and then convert it to a &str:
matches.get_one::<String>("arg").map(|s| s.as_str());

Related

Rust - the trait `StdError` is not implemented for `OsString`

I'm writing some Rust code which uses the ? operator. Here is a few lines of that code:
fn files() -> Result<Vec<std::string::String>, Box<Error>> {
let mut file_paths: Vec<std::string::String> = Vec::new();
...
file_paths.push(pathbuf.path().into_os_string().into_string()?);
...
Ok(file_paths)
}
However, even though I'm using ? on a Result it is giving me the following error:
`the trait `StdError` is not implemented for `OsString`.
This is contrary to the Rust documentation here, which states that:
The ? is shorthand for the entire match statements we wrote earlier. In other words, ? applies to a Result value, and if it was an Ok, it unwraps it and gives the inner value. If it was an Err, it returns from the function you're currently in.
I've confirmed that pathbuf.path().into_os_string().into_string() is of type Result, because when I remove the ?, I get the following compiler error:
expected struct `std::string::String`, found enum `std::result::Result`
(since file_paths is a Vector of strings, not Results).
Is this a bug with the Rust language or documentation?
In fact I tried this without pushing to the Vector, but simply initializing a variable with the value of pathbuf.path().into_os_string().into_string()?, and I got the same error.
The function OsString::into_string is a little unusual. It returns a Result<String, OsString> - so the Err variant is actually not an error.
In the event that the OsString cannot be converted into a regular string, then the Err variant is returned, containing the original string.
Unfortunately this means you cannot use the ? operator directly. However, you can use map_err to map the error variant into an actual error, like this:
file_paths.push(
pathbuf.path()
.into_os_string()
.into_string().
.map_err(|e| InvalidPathError::new(e))?
);
In the above example, InvalidPathError might be your own error type. You could also use an error type from the std library.

Slice of String vs Slice &String

I was reading the doc from rust lang website and in chapter 4 they did the following example:
let s = String::from("hello world");
let hello = &s[0..5];
let world = &s[6..11];
hello is of type &str that I created from a variable s of type String.
Some rows below they define the following function:
fn first_word(s: &String) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
This time s is of type &String but still &s[0..i] gave me a &str slice.
How is it possible? I thought that the correct way to achieve this would be something like &((*str)[0..i]).
Am I missing something? Maybe during the [0..i] operation Rust auto deference the variable?
Thanks
Maybe during the [0..i] operation Rust auto deference the variable?
This is exactly what happens. When you call methods/index a reference, it automatically dereferences before applying the method. This behavior can also be manually implemented with the Deref trait. String implements the Deref with a target of str, which means when you call str methods on String. Read more about deref coercion here.
It's important to realize what happens with &s[1..5], and that it's &(s[1..5]), namely, s[1..5] is first first evaluated, this returns a value of type str, and a reference to that value is taken. In fact, there's even more indirection: x[y] in rust is actually syntactic sugar for *std::ops::Index::index(x,y). Note the dereference, as this function always returns a reference, which is then dereferenced by the sugar, and then it is referenced again by the & in our code — naturally, the compiler will optimize this and ensure we are not pointlessly taking references to only dereference them again.
It so happens that the String type does support the Index<Range<usize>> trait and it's Index::output type is str.
It also happens that the str type supports the same, and that it's output type is also str, viā a blanket implementation of SliceIndex.
On your question of auto-dereferencing, it is true that Rust has a Deref trait defined on String as well so that in many contexts, such as this one, &String is automatically cast to &str — any context that accepts a &str also accepts a &String, meaning that the implementation on Index<usize> on String is actually for optimization to avoid this indirection. If it not were there, the code would still work, and perhaps the compiler could even optimize the indirection away.
But that automatic casting is not why it works — it simply works because indexing is defined on many different types.
Finally:
I thought that the correct way to achieve this would be something like &((*str)[0..i]).
This would not work regardless, a &str is not the same as a &String and cannot be dereferenced to a String like a &String. In fact, a &str in many ways is closer to a String than it is to a &String. a &str is really just a fat pointer to a sequence of unicode bytes, also containing the length of said sequence in the second word; a String is, if one will, an extra-fat pointer that also contains the current capacity of the buffer with it, and owns the buffer it points to, so it can delete and resize it.

What does &* combined together do in Rust?

I was reading through the book section about Strings and found they were using &* combined together to convert a piece of text. The following is what it says:
use std::net::TcpStream;
TcpStream::connect("192.168.0.1:3000"); // Parameter is of type &str.
let addr_string = "192.168.0.1:3000".to_string();
TcpStream::connect(&*addr_string); // Convert `addr_string` to &str.
In other words, they are saying they are converting a String to a &str. But why is that conversion done using both of the aforementioned signs? Should this not be done using some other method? Does not the & mean we are taking its reference, then using the * to dereference it?
In short: the * triggers an explicit deref, which can be overloaded via ops::Deref.
More Detail
Look at this code:
let s = "hi".to_string(); // : String
let a = &s;
What's the type of a? It's simply &String! This shouldn't be very surprising, since we take the reference of a String. Ok, but what about this?
let s = "hi".to_string(); // : String
let b = &*s; // equivalent to `&(*s)`
What's the type of b? It's &str! Wow, what happened?
Note that *s is executed first. As most operators, the dereference operator * is also overloadable and the usage of the operator can be considered syntax sugar for *std::ops::Deref::deref(&s) (note that we recursively dereferencing here!). String does overload this operator:
impl Deref for String {
type Target = str;
fn deref(&self) -> &str { ... }
}
So, *s is actually *std::ops::Deref::deref(&s), in which the deref() function has the return type &str which is then dereferenced again. Thus, *s has the type str (note the lack of &).
Since str is unsized and not very handy on its own, we'd like to have a reference to it instead, namely &str. We can do this by adding a & in front of the expression! Tada, now we reached the type &str!
&*s is rather the manual and explicit form. Often, the Deref-overload is used via automatic deref coercion. When the target type is fixed, the compiler will deref for you:
fn takes_string_slice(_: &str) {}
let s = "hi".to_string(); // : String
takes_string_slice(&s); // this works!
In general, &* means to first dereference (*) and then reference (&) a value. In many cases, this would be silly, as we'd end up at the same thing.
However, Rust has deref coercions. Combined with the Deref and DerefMut traits, a type can dereference to a different type!
This is useful for Strings as that means that they can get all the methods from str, it's useful for Vec<T> as it gains the methods from [T], and it's super useful for all the smart pointers, like Box<T>, which will have all the methods of the contained T!
Following the chain for String:
String --deref--> str --ref--> &str
Does not the & mean we are taking its reference, then using the * to dereference it?
No, your order of operations is backwards. * and & associate to the right. In this example, dereferencing is first, then referencing.
I think now you can do this &addr_string
(from a comment)
Sometimes, this will do the same thing. See What are Rust's exact auto-dereferencing rules? for the full details, but yes, a &String can be passed to a function that requires a &str. There are still times where you need to do this little dance by hand. The most common I can think of is:
let name: Option<String> = Some("Hello".to_string());
let name2: Option<&str> = name.as_ref().map(|s| &**s);
You'll note that we actually dereference twice:
&String -->deref--> String --deref--> str --ref--> &str
Although this case can now be done with name.as_ref().map(String::as_str);

How can I return a String Vector?

How can I return a String Vector in Rust? I tried:
fn test_vec() -> Vec<&str> {
vec!("foo", "bar")
}
The compiler says something about lifetimes, but I'm not sure my problem is really about lifetimes:
src/main.rs:9:22: 9:26 error: missing lifetime specifier [E0106]
I'm a bit lost, I think I misunderstood (or forgot to learn) something.
A &str is not a String. It is a "string slice", meaning a kind of pointer into a String or something equivalent that is stored somewhere else. In your case you are using string literals (using quotes gives you string literals). String literals are of the type &'static str, because they are stored in the same place where the compiled code is stored and thus are available for the 'static lifetime, which means for (at least) the entire runtime of your program.
So the easy fix is to have your method return that specific type: &'static str.
The compiler cannot infer a lifetime for the returned reference, because your function does not take any arguments of reference type. The only way the compiler will infer a lifetime in a function's signature, is by assuming that if you are returning a reference, it needs to live shorter than the argument it's referring to. There's more information in The Book

How do I convert from an integer to a string?

I am unable to compile code that converts a type from an integer to a string. I'm running an example from the Rust for Rubyists tutorial which has various type conversions such as:
"Fizz".to_str() and num.to_str() (where num is an integer).
I think the majority (if not all) of these to_str() function calls have been deprecated. What is the current way to convert an integer to a string?
The errors I'm getting are:
error: type `&'static str` does not implement any method in scope named `to_str`
error: type `int` does not implement any method in scope named `to_str`
Use to_string() (running example here):
let x: u32 = 10;
let s: String = x.to_string();
println!("{}", s);
You're right; to_str() was renamed to to_string() before Rust 1.0 was released for consistency because an allocated string is now called String.
If you need to pass a string slice somewhere, you need to obtain a &str reference from String. This can be done using & and a deref coercion:
let ss: &str = &s; // specifying type is necessary for deref coercion to fire
let ss = &s[..]; // alternatively, use slicing syntax
The tutorial you linked to seems to be obsolete. If you're interested in strings in Rust, you can look through the strings chapter of The Rust Programming Language.

Resources