I've got this simple parsing function
use std::collections::BTreeMap;
fn parse_kv(data: &str) -> BTreeMap<String, String> {
data.split('&')
.map(|kv| kv.split('='))
.map(|mut kv| (kv.next().unwrap().into(), kv.next().unwrap().into()))
.collect()
}
#[test]
fn parse_kv_test() {
let result = parse_kv("test1=1&test2=2");
assert_eq!(result["test1"], "1");
assert_eq!(result["test2"], "2");
}
It works fine and all, but I want to have Option or Result return type like so:
fn parse_kv(data: &str) -> Option<BTreeMap<String, String>>
This implementation:
fn parse_kv(data: &str) -> Option<BTreeMap<String, String>> {
Some(data.split('&')
.map(|kv| kv.split('='))
.map(|mut kv| (kv.next()?.into(), kv.next()?.into()))
.collect())
}
Unfortunately gives the following error:
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> src/ecb_cut_paste.rs:23:24
|
23 | .map(|mut kv| (kv.next()?.into(), kv.next()?.into()))
| ^^^^^^^^^^ cannot use the `?` operator in a function that returns `(_, _)`
|
= help: the trait `std::ops::Try` is not implemented for `(_, _)`
= note: required by `std::ops::Try::from_error`
Is it somehow possible to use ? operator inside closure to return None from such function? If not, how would I need to handle idiomatically such case?
The issue here is that the closure itself is a function, so using ? will return from the closure instead of the outer function. This can still be used to implement the function the way you want, however:
use std::collections::BTreeMap;
fn parse_kv(data: &str) -> Option<BTreeMap<String, String>> {
data.split('&')
.map(|kv| kv.split('='))
.map(|mut kv| Some((kv.next()?.into(), kv.next()?.into())))
.collect()
}
#[test]
fn parse_kv_test() {
let result = parse_kv("test1=1&test2=2").unwrap();
assert_eq!(result["test1"], "1");
assert_eq!(result["test2"], "2");
let result2 = parse_kv("test1=1&test2");
assert_eq!(result2, None);
}
There are a couple points to note here: First, the question marks and Some(...) in the second map invocation mean you have an iterator of Option<(String, String)> - type inference figures this out for you.
The next point of note is that collect() can automatically convert Iterator<Option<T>> into Option<Collection<T>> (same with Result - relevant documentation here). I added a test demonstrating that this works.
One other thing to be aware of is that using collect in this way still allows short-circuiting. Once the first None is yielded by the iterator, collect will immediately return with None, rather than continuing to process each element.
Related
Having diffuclty grasping the concept of generics. How can I return a vector from a function with a generic value? I'd like to input text and the output be either a vector of eithers strings or integers. However, the compiler gives me error[E0277]: <T as FromStr>::Err doesn't implement Debug. It's telling me that I need to implement the 'Debug'trait? But I don't understand why. How can I simply return a vector of an arbitrary type?
use std::str::FromStr;
fn main() {
let a: Vec<u32> = text_to_vec("1 2 3 4");
}
fn text_to_vec<T: FromStr>(text: &str) -> Vec<T> {
let mut list = Vec::new();
for word in text.split(" "){
if let w = word {
let w = w.parse().unwrap();
list.push(w);
}
}
return list;
}
I'm expecting to get a vector of u32 integers in this case.
Here is the error:
error[E0277]: `<T as FromStr>::Err` doesn't implement `Debug`
--> src/main.rs:12:21
|
12 | let w = w.parse().unwrap();
| ^^^^^^^^^ ------ required by a bound introduced by this call
| |
| `<T as FromStr>::Err` cannot be formatted using `{:?}` because it doesn't implement `Debug`
|
= help: the trait `Debug` is not implemented for `<T as FromStr>::Err`
note: required by a bound in `Result::<T, E>::unwrap`
help: consider further restricting the associated type
|
7 | fn text_to_vec<T: FromStr>(text: &str) -> Vec<T> where <T as FromStr>::Err: Debug {
| ++++++++++++++++++++++++++++++++
You should read the full error message since it addresses where the Debug requirement comes from and even provides a solution.
Calling .unwrap() requires the error type implements Debug since it will display a message with the error if one occurred.
The last part of the error suggests adding a where clause to constrain that the error type returned from .parse() implements Debug.
Another solution would be to ignore or log the error yourself if you don't want to add the constraint. Something using a match would work nicely:
match w.parse() {
Ok(w) => list.push(w),
Err(_) => println!("failed to parse {w}"),
}
The compiler error here is caused by the unwrap. unwrap creates a panic, which by default attempts to print out the error. This is why it requires that <T as FromStr>::Err implements Debug. However, your function does not require that.
What I'd recommend is to implement your function using iterators, and returning a Result instead. Then you can unwrap elsewhere.
use std::str::FromStr;
fn main() {
let a: Vec<u32> = text_to_vec("1 2 3 4").unwrap();
}
fn text_to_vec<T: FromStr>(text: &str) -> Result<Vec<T>, <T as FromStr>::Err> {
text.split(" ").map(|word| word.parse()).collect()
}
Otherwise, you will have to add that bound to your function:
use std::fmt::Debug;
fn text_to_vec_unwrap<T>(text: &str) -> Vec<T>
where
T: FromStr,
<T as FromStr>::Err: Debug,
{
text.split(" ").map(|word| word.parse().unwrap()).collect()
}
I have a function which looks like
fn do_stuff(values: HashSet<String>) {
// Count stuff
for s in values.iter() {
prepare(s);
}
// Process stuff
for s in values.iter() {
process(s);
}
}
This works fine. For a unit test, I want to pass a two value collection where the elements are passed in a known order. (Processing them in the other order won't test the case I am trying to test.) HashSet doesn't guarantee an order, so I would like to pass a Vec instead.
I would like to change the argument to Iterable, but it appears that only IntoIter exists. I tried
fn do_stuff<C>(values: C)
where C: IntoIterator<Item=String>
{
// Count stuff
for s in values {
prepare(s);
}
// Process stuff
for s in values {
process(s);
}
}
which fails because the first iteration consumes values. The compiler suggests borrowing values, but
fn do_stuff<C>(values: C)
where C: IntoIterator<Item=String>
{
// Count stuff
for s in &values {
prepare(s);
}
// Process stuff
for s in values {
process(s);
}
}
fails because
the trait Iterator is not implemented for &C
I could probably make something with clone work, but the actual set will be large and I would like to avoid copying it if possible.
Thinking about that, the signature probably should be do_stuff(values: &C), so if that makes the problem simpler, then that is an acceptable solution.
SO suggests Writing a generic function that takes an iterable container as parameter in Rust as a related question, but that is a lifetime problem. I am not having problems with lifetimes.
It looks like How to create an `Iterable` trait for references in Rust? may actually be the solution. But I'm having trouble getting it to compile.
My first attempt is
pub trait Iterable {
type Item;
type Iter: Iterator<Item = Self::Item>;
fn iterator(&self) -> Self::Iter;
}
impl Iterable for HashSet<String> {
type Item = String;
type Iter = HashSet<String>::Iterator;
fn iterator(&self) -> Self::Iter {
self.iter()
}
}
which fails with
error[E0223]: ambiguous associated type
--> src/file.rs:178:17
|
178 | type Iter = HashSet<String>::Iterator;
| ^^^^^^^^^^^^^^^^^^^^^^^^^ help: use fully-qualified syntax: `<HashSet<std::string::String> as Trait>::Iterator`
Following that suggestion:
impl Iterable for HashSet<String> {
type Item = String;
type Iter = <HashSet<std::string::String> as Trait>::Iterator;
fn iterator(&self) -> Self::Iter {
self.iter()
}
}
failed with
error[E0433]: failed to resolve: use of undeclared type `Trait`
--> src/file.rs:178:50
|
178 | type Iter = <HashSet<std::string::String> as Trait>::Iterator;
| ^^^^^ use of undeclared type `Trait`
The rust documents don't seem to include Trait as a known type. If I replace Trait with HashSet, it doesn't recognize Iterator or IntoIter as the final value in the expression.
Implementation of accepted answer
Attempting to implement #eggyal answer, I was able to get this to compile
use std::collections::HashSet;
fn do_stuff<I>(iterable: I)
where
I: IntoIterator + Copy,
I::Item: AsRef<str>,
{
// Count stuff
for s in iterable {
prepare(s.as_ref());
}
// Process stuff
for s in iterable {
process(s.as_ref());
}
}
fn prepare(s: &str) {
println!("prepare: {}", s)
}
fn process(s: &str) {
println!("process: {}", s)
}
#[cfg(test)]
mod test_cluster {
use super::*;
#[test]
fn doit() {
let vec: Vec<String> = vec!["a".to_string(), "b".to_string(), "c".to_string()];
let set = vec.iter().cloned().collect::<HashSet<_>>();
do_stuff(&vec);
do_stuff(&set);
}
}
which had this output
---- simple::test_cluster::doit stdout ----
prepare: a
prepare: b
prepare: c
process: a
process: b
process: c
prepare: c
prepare: b
prepare: a
process: c
process: b
process: a
IntoIterator is not only implemented by the collection types themselves, but in most cases (including Vec and HashSet) it is also implemented by their borrows (yielding an iterator of borrowed items). Moreover, immutable borrows are always Copy. So you can do:
fn do_stuff<I>(iterable: I)
where
I: IntoIterator + Copy,
I::Item: AsRef<str>,
{
// Count stuff
for s in iterable {
prepare(s);
}
// Process stuff
for s in iterable {
process(s);
}
}
And this would then be invoked by passing in a borrow of the relevant collection:
let vec = vec!["a", "b", "c"];
let set = vec.iter().cloned().collect::<HashSet<_>>();
do_stuff(&vec);
do_stuff(&set);
Playground.
However, depending on your requirements (whether all items must first be prepared before any can be processed), it may be possible in this case to combine the preparation and processing into a single pass of the iterator.
Iterators over containers can be cloned if you want to iterate the container twice, so accepting an IntoIterator + Clone should work for you. Example code:
fn do_stuff<I>(values: I)
where
I: IntoIterator + Clone,
{
// Count stuff
for s in values.clone() {
prepare(s);
}
// Process stuff
for s in values {
process(s);
}
}
You can now pass in e.g. either a hash set or a vector, and both of them can be iterated twice:
let vec = vec!["a", "b", "c"];
let set: HashSet<_> = vec.iter().cloned().collect();
do_stuff(vec);
do_stuff(set);
(Playground)
i have the following code snippet which implements some kind of Emitter Struct:
type Callback<'a> = Option<&'a mut dyn FnMut()>;
struct Emitter<'a> {
cb: Callback<'a>
}
impl<'a> Emitter<'a> {
fn emit(&mut self) {
if self.cb.is_some() {
let f = self.cb.unwrap();
f()
}
}
}
fn main() {
let mut cb = || println!("test");
let mut e = Emitter {
cb : Some(&mut cb)
};
e.emit();
}
The emit() function tries to run the saved callback clojure. But i cannot wrap my head around how to run the callback, since the code produces the following error:
--> src/main.rs:11:15
|
11 | let f = self.cb.unwrap();
| ^^^^^^^
| |
| move occurs because `self.cb` has type `Option<&mut dyn FnMut()>`, which does not implement the `Copy` trait
| help: consider borrowing the `Option`'s content: `self.cb.as_ref()`
Appreciate some help :)
Here is the snippet on replit: https://replit.com/#lutzer/RustEmitterTest
What's going on here is that your line
let f = self.cb.unwrap();
would want to move the closure out of the Option enum. This operation consumes that enum, which isn't allowed for things that belong to a struct.
Here is a simpler example to show what I mean:
fn main() {
let an_option = Some(String::from("Woot!");
let the_value = an_option.unwrap();
println!("The value is {}", the_value);
println!("The option is {:?}", an_option); // error here! Can't use an_option any more!!!
https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4a4a3660b68ebada99113db5165b6e76
So if you take ownership of something stored inside the Some part of an Option, via unwrap, then the whole Option gets moved out. You can see that in the signature of unwrap:
pub const fn unwrap(self) -> T
Note how it says self, and not &self or &mut self. That means, after calling unwrap, that the enum gets consumed and cannot be used any more, unless the value inside the Some part can simply be copied (If you replace the String in my example with, say, an integer, it will compile without issue).
The comment by Omer Erden then explains a way around that: Ask the Option to give you a mutable reference instead via as_mut.
Or skip all that directly and use the map method of option, which you can use to do something if the option is Some and just not do anything if it's None.
I'm writing a library that uses generators to hold continuations. Sometimes I want to pass a closure with no suspension points, or no yields, but the compiler complains that the closure doesn't implement the Generator trait.
I want to compile the following code without adding a yield to the closure; how can I let the compiler treat the closure as a generator?
#![feature(generators, generator_trait)]
use std::ops::Generator;
fn library_func(mut g: Box<dyn Generator<Yield = (), Return = ()>>) {
let x = unsafe { g.resume() };
println!("{:?}", x);
}
fn main() {
// a closure without yield
let x = Box::new(|| {
// uncommenting this line makes it compile, but changes the behavior
// yield ();
});
library_func(x);
}
error[E0277]: the trait bound `[closure#src/main.rs:12:22: 15:6]: std::ops::Generator` is not satisfied
--> src/main.rs:17:18
|
17 | library_func(x);
| ^ the trait `std::ops::Generator` is not implemented for `[closure#src/main.rs:12:22: 15:6]`
|
= note: required for the cast to the object type `dyn std::ops::Generator<Yield=(), Return=()>`
A closure isn't a generator, so the compiler can't really treat it as one. It is unclear whether the generator you wish to implement is supposed to return or yield the return value of the function; assuming you want the former, you can use a yield statement after a return statement to create a generator that does not yield:
let x = Box::new(|| {
return;
yield;
});
If you need this frequently, you can also wrap this in a function:
fn into_generator<F, T>(f: F) -> impl Generator<Yield = (), Return = T>
where
F: FnOnce() -> T,
{
#[allow(unreachable_code)]
|| {
return f();
yield;
}
}
(Full code on the playground)
Using this file:
use std::env;
fn main() {
println!("{}", env::args().nth(3)?);
}
I get this error:
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `std::ops::Try`)
--> src/main.rs:4:20
|
4 | println!("{}", env::args().nth(3)?);
| ^^^^^^^^^^^^^^^^^^^ cannot use the `?` operator in a function that returns `()`
|
= help: the trait `std::ops::Try` is not implemented for `()`
= note: required by `std::ops::Try::from_error`
However this is confusing because nth does return Option:
fn nth(&mut self, n: usize) -> Option<Self::Item>
Am I misunderstanding the documentation or is this a bug?
The return type of main must implement std::process::Termination(currently it's an unstable trait). If you look at the end of the documentation, you will see:
impl Termination for !
impl Termination for ()
impl Termination for ExitCode
impl<E: Debug> Termination for Result<!, E>
impl<E: Debug> Termination for Result<(), E>
If you want to return an Option you must implement the trait on it. This is not practical because you can't implement a trait on foreign type, so the best solution is to convert Option<T> to Result<T, E>:
use std::env;
fn main() -> Result<(), Box<std::error::Error>> {
println!("{}", env::args().nth(3).ok_or("Missing argument")?);
Ok(())
}
See also:
Why do try!() and ? not compile when used in a function that doesn't return Option or Result?
The ? operator will cause the function containing it to return None if the value the ? is applied to is None.
This means you can write
fn not_main() -> Option<()> {
println!("{}", std::env::args().nth(3)?);
Ok(())
}
since nth returns an Option<Item> and not_main returns an Option<()>.
However, your main does not return an Option, hence ? can't work inside it.
How you work around this will depend on what you want to do in the case of a missing argument. The most brutal solution is to unwrap instead - which will cause your code to panic.
fn main() {
println!("{}", env::args().nth(3).unwrap())
}
An alternative is to match and handle the missing case
fn main() {
match std::env::args().nth(3) {
Some(ref v) => println!("{}", v),
None => println!("Missing argument"),
}
}
Since Option supports Debug you could print the debug version - which will output None, or Some("arg3").
fn main() {
println!("{:?}", std::env::args().nth(3));
}
If you really want to use ? on a Option value in main, you probably need to implement you own Option.
In your case, nothing::Probably is a better Option.
Example (you need nightly toolchain to run it):
use nothing::{Nothing, Probably, Something};
fn get_args() -> Probably<Vec<String>> {
match std::env::args().skip(1).collect::<Vec<String>>() {
args # _ if args.len() > 0 => Something(args),
_ => Nothing,
}
}
fn main() -> Probably<Vec<String>> {
let some_args = get_args();
println!("some_args = {some_args:?}");
let args = some_args?; // <- it returns here if some_args is Nothing
println!("args = {args:?}");
Something(args)
}
It works because Probably implements std::process::Termination so you can return it from you main function. Additionally it implements std::ops::Try so you can use ? on it.