'Need help understanding error when converting env::args() to vec of &str
Rust newbie here: I'm trying to take arguments from the commandline and then match against them. Apparently match needs to match on &strs rather than Strings, so I'm trying to use this answer to convert env::args(), like so:
let args = env::args().map(AsRef::as_ref).collect::<Vec<_>>();
match &args[1..] {
["hello"] => println!("Hello, world!"),
// ...
_ => eprintln!("unknown command: {:?}", args),
}
However, the as_ref part is failing to compile:
error[E0631]: type mismatch in function arguments
--> src/main.rs:4:32
|
4 | let args = env::args().map(AsRef::as_ref).collect::<Vec<_>>();
| --- ^^^^^^^^^^^^^
| | |
| | expected signature of `fn(String) -> _`
| | found signature of `for<'r> fn(&'r _) -> _`
| required by a bound introduced by this call
I'm having trouble understanding this error:
- When it's talking about the expected and found signatures, is it referring to the
mapparameter, or the passed-in function? - How do I understand
for<'r> fn(&'r _) -> _? I've not seen this syntax before (especially theforpart) so I don't know how to interpret the problem.
Solution 1:[1]
The "expected" signature is the signature of the map() parameter as declared, while the "found" signature is the signature of the function you passed. The map() function is declared as:
fn map<B, F>(self, f: F) -> Map<Self, F>?
where
F: FnMut(Self::Item) -> B,
env::args() returns env::Args, which implements Iterator<Item = String>. So the expected signature is some function (a type implementing FnMut, to be precise, so some closures are also accepted) of signature fn(String) -> SomeType.
Your function, on the other hand, is AsRef::as_ref, which is defined as:
pub trait AsRef<T: ?Sized> {
fn as_ref(&self) -> &T;
}
So it takes reference to some type (self) and returns reference to some another type (T). In your case, you want self to be String and T to be str, but the compiler doesn't know that yet: it has to infer that. From its point of view, it has the signature fn(&_) -> &_ (_ stands for "inference type", or "figure it out").
Or to be more precise for<'a> fn(&'a _) -> &'a _. This is called HRTB (Higher-Order Traits Bounds. See also How does for<> syntax differ from a regular lifetime bound?), but is not really relevant here. It's just the compiler got confused because it expected a function that takes String and got a function that takes some reference.
The solution, of course, is to use a function that takes String. You can use a closure for that. But that reveals another error (playground):
let args = env::args().map(|v| v.as_ref()).collect::<Vec<_>>();
error[E0515]: cannot return reference to function parameter `v`
--> src/main.rs:4:36
|
4 | let args = env::args().map(|v| v.as_ref()).collect::<Vec<_>>();
Because the string is dropped at the end of the closure, but we return a reference to it as &str. There are multiple ways to fix that, depending on the use case. For example, if you only want to use the &str in the same function, you can collect the result of env::args() to a Vec:
let args = Vec::from_iter(env::args());
let args = args.iter().map(|v| v.as_ref()).collect::<Vec<_>>();
By the way, because iter() returns an iterator over &String, now you can use AsRef::as_ref directly:
let args = Vec::from_iter(env::args());
let args = args.iter().map(AsRef::as_ref).collect::<Vec<_>>();
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Chayim Friedman |
