In the Rust By Example book show us a way to handle errors in the map method of Iterator:
let strings = vec!["93", "tofu", "18"];
let numbers: Result<Vec<_>, _> = strings.into_iter().map(|s| s.parse::<i32>()).collect();
println!("Results: {:?}", numbers);
Is it a similar way to deal with errors in Option like the following?
let a: Option<&str> = Some("tofu");
let b: Result<Option<i32>, _> = a.map(|a| a.parse::<i32>());
println!("Results: {:?}", b);
There is a handy method called .transpose() that can convert an Option<Result<T, E>> into a Result<Option<T>, E> (or the reverse):
let a: Option<&str> = Some("tofu");
let b: Result<Option<i32>, _> = a.map(|a| a.parse::<i32>()).transpose();
println!("Results: {:?}", b);
Related
Here is my original simplified code, I want to use a global variable instead of the variables in separate functions. What's the suggestion method in rust?
BTW, I've tried to use global or change to function parameter, both are nightmare for a beginner. Too difficult to solve the lifetime & variable type cast issue.
This simple program is only a single thread tool, so, in C language, it is not necessary the extra mutex.
// version 1
use std::collections::BTreeMap;
// Trying but failed
// let mut guess_number = BTreeMap::new();
// | ^^^ expected item
fn read_csv() {
let mut guess_number = BTreeMap::new();
let lines = ["Tom,4", "John,6"];
for line in lines.iter() {
let split = line.split(",");
let vec: Vec<_> = split.collect();
println!("{} {:?}", line, vec);
let number: u16 = vec[1].trim().parse().unwrap();
guess_number.insert(vec[0], number);
}
for (k, v) in guess_number {
println!("{} {:?}", k, v);
}
}
fn main() {
let mut guess_number = BTreeMap::new();
guess_number.insert("Tom", 3);
guess_number.insert("John", 7);
if guess_number.contains_key("John") {
println!("John's number={:?}", guess_number.get("John").unwrap());
}
read_csv();
}
To explain how hard it is for a beginner, by pass parameter
// version 2
use std::collections::BTreeMap;
fn read_csv(guess_number: BTreeMap) {
// ^^^^^^^^ expected 2 generic arguments
let lines = ["Tom,4", "John,6"];
for line in lines.iter() {
let split = line.split(",");
let vec: Vec<_> = split.collect();
println!("{} {:?}", line, vec);
let number: u16 = vec[1].trim().parse().unwrap();
guess_number.insert(vec[0], number);
}
}
fn main() {
let mut guess_number = BTreeMap::new();
guess_number.insert("Tom", 3);
guess_number.insert("John", 7);
if guess_number.contains_key("John") {
println!("John's number={:?}", guess_number.get("John").unwrap());
}
read_csv(guess_number);
for (k, v) in guess_number {
println!("{} {:?}", k, v);
}
}
After some effort, try & error to get the possible work type BTreeMap<&str, i32>
// version 3
use std::collections::BTreeMap;
fn read_csv(guess_number: &BTreeMap<&str, i32>) {
// let mut guess_number = BTreeMap::new();
let lines = ["Tom,4", "John,6"];
for line in lines.iter() {
let split = line.split(",");
let vec: Vec<_> = split.collect();
println!("{} {:?}", line, vec);
let number: i32 = vec[1].trim().parse().unwrap();
guess_number.insert(vec[0], number);
}
for (k, v) in guess_number {
println!("{} {:?}", k, v);
}
}
fn main() {
let mut guess_number: BTreeMap<&str, i32> = BTreeMap::new();
guess_number.insert("Tom", 3);
guess_number.insert("John", 7);
if guess_number.contains_key("John") {
println!("John's number={:?}", guess_number.get("John").unwrap());
}
read_csv(&guess_number);
for (k, v) in guess_number {
println!("{} {:?}", k, v);
}
}
will cause following error
7 | fn read_csv(guess_number: &BTreeMap<&str, i32>) {
| -------------------- help: consider changing this to be a mutable reference: `&mut BTreeMap<&str, i32>`
...
16 | guess_number.insert(vec[0], number);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `guess_number` is a `&` reference, so the data it refers to cannot be borrowed as mutable
The final answer (seems not suggest use global in Rust, so use 'mutable reference').
// version 4
use std::collections::BTreeMap;
fn read_csv(guess_number: &mut BTreeMap<&str, i32>) {
let lines = ["Tom,4", "John,6"];
for line in lines.iter() {
let split = line.split(",");
let vec: Vec<_> = split.collect();
println!("{} {:?}", line, vec);
let number: i32 = vec[1].trim().parse().unwrap();
guess_number.insert(vec[0], number);
}
}
fn main() {
let mut guess_number: BTreeMap<&str, i32> = BTreeMap::new();
guess_number.insert("Tom", 3);
guess_number.insert("John", 7);
if guess_number.contains_key("John") {
println!("John's number={:?}", guess_number.get("John").unwrap());
}
read_csv(&mut guess_number);
for (k, v) in guess_number {
println!("{} {:?}", k, v);
}
}
This question is not specific to BTreeMaps but for pretty much all data types, such as numbers, strings, vectors, enums, etc.
If you want to pass a variable (value) from one function to another, you can do that in various ways in Rust. Typically you either move the value or you pass a reference to it. Moving is something quite specific to Rust and its ownership model. This is really essential, so if you have serious intentions to learn Rust, I strongly suggest you read the chapter Understanding Ownership from "the book". Don't get discouraged if you don't understand it from one reading. Spend as much time as needed, as you really can't move forward w/o this knowledge.
As for global variables, there are very few situations where they should be used. In Rust using global variables is slightly more difficult, compared to most other languages. This thread is quite useful, although you might find it a bit difficult to comprehend. My advice to a beginner would be to first fully understand the basic concept of moving and passing references.
I would like to write some code in a "functional programming" style.
However, I start with an Iterator of Results and I only want to apply the function to the Ok items. Furthermore, I want to stop the iteration on the first error (however, I'd be open to different behavior).
So far, I am using a nested map() pattern: <iter>.map(|l| l.map(replace)). I think this is extremely ugly.
Using the nightly "result_flattening", I can flatten each nested Result<Result<T, E>, E> into a Result<T, E>. Using eyre::Context I convert the different Error types into an eyre::Report error type. All of this feels quite clumsy.
What is an elegant way to write this in Rust?
Minimal Working Example
#![feature(result_flattening)]
use std::io::BufRead;
use eyre::Context;
fn main() {
let data = std::io::Cursor::new(b"FFBFFFBLLL\nBFBFBBFRLR\nFFFBFFBLLL");
let seats: Result<Vec<_>, _> = data
.lines()
.map(|l| l.map(replace).context("force eyre"))
.map(|l| l.map(|s| u32::from_str_radix(&s, 2).context("force eyre")))
.map(|r| r.flatten())
.collect();
println!("{:#?}", seats);
}
fn replace(line: String) -> String {
line.replace('F', "0")
.replace('B', "1")
.replace('L', "0")
.replace('R', "1")
}
Further References:
How do I stop iteration and return an error when Iterator::map returns a Result::Err?
Result implements FromIter.
result_flatten: https://doc.rust-lang.org/std/result/enum.Result.html?search=#method.flatten, https://github.com/rust-lang/rust/issues/70142 (I'm using rustc 1.49.0-nightly (ffa2e7ae8 2020-10-24))
lines() returns Results: https://doc.rust-lang.org/std/io/trait.BufRead.html#method.lines
Since you discard the error type anyway, you can avoid eyre entirely and use .ok to convert the Result into an Option, then just work with Option's and_then to avoid flattening every time:
let seats: Option<Vec<_>> = data
.lines()
.map(|l| l.ok())
.map(|l| l.map(replace))
.map(|l| l.and_then(|s| u32::from_str_radix(&s, 2).ok()))
// if you want to keep chaining
.map(|l| l.and_then(|s| some_result_function(&s).ok()))
.collect();
If you want to just skip over the errors, a much more elegant solution exists with filter_map:
let seats: Vec<_> = data
.lines()
.filter_map(|l| l.ok())
.map(replace)
.filter_map(|l| u32::from_str_radix(&l, 2).ok())
.collect();
If you want to maintain errors, then box the errors into a Box<dyn Error> to account for different types:
use std::error::Error;
// later in the code
let seats: Result<Vec<_>, Box<dyn Error>> = data
.lines()
.map(|x| x.map_err(|e| Box::new(e) as _))
.map(|l| l.map(replace))
.map(|l| l.and_then(|s| u32::from_str_radix(&s, 2).map_err(|e| Box::new(e) as _)))
.collect();
If you don't like the repeated .map_err(|e| Box::new(e) as _), then make a trait for it:
use std::error::Error;
trait BoxErr {
type Boxed;
fn box_err(self) -> Self::Boxed;
}
impl<T, E: Error + 'static> BoxErr for Result<T, E> {
type Boxed = Result<T, Box<dyn Error>>;
fn box_err(self) -> Self::Boxed {
self.map_err(|x| Box::new(x) as Box<dyn Error>)
}
}
// later in the code
let seats: Result<Vec<_>, Box<dyn Error>> = data
.lines()
.map(|x| x.box_err())
.map(|l| l.map(replace))
.map(|l| l.and_then(|s| u32::from_str_radix(&s, 2).box_err()))
.collect();
What's an efficient way to convert a result of type &[Box<[u8]>] into something more readily consumed like String or &str?
An example function is the txt_data() method from trust_dns_proto::rr:rdat::txt::TXT.
I've tried several things that seem to go nowhere, like:
fn main() {
let raw: &[Box<[u8]>] = &["Hello", " world!"]
.iter()
.map(|i| i.as_bytes().to_vec().into_boxed_slice())
.collect::<Vec<_>>();
let value = raw.iter().map(|s| String::from(*s)).join("");
assert_eq!(value, "Hello world!");
}
Where raw is of that type.
There is no way to convert an array of octets to str directly cause the data is split up. So a String look like a good candidate.
I would use str::from_utf8() combined with try_fold():
use std::str;
fn main() {
let raw: &[Box<[u8]>] = &["Hello", " world!"]
.iter()
.map(|i| i.as_bytes().to_vec().into_boxed_slice())
.collect::<Vec<_>>();
let value = raw
.iter()
.map(|i| str::from_utf8(i))
.try_fold(String::new(), |a, i| {
i.map(|i| {
let mut a = a;
a.push_str(i);
a
})
});
assert_eq!(value.as_ref().map(|x| x.as_str()), Ok("Hello world!"));
}
It looks like the solution is this:
let value: String = raw
.iter()
.map(|s| String::from_utf8((*s).to_vec()).unwrap())
.collect::<Vec<String>>()
.join("");
Where the key is from_utf8() and the (*s).to_vec() suggested by rustc.
Let's say that I have an Option<String> and want to pattern match to get a mutable reference to the String. I can do the following (_a needs to be mutable):
let mut _a: Option<String> = Some(String::from("foo"));
if let Some(ref mut aa) = _a {
aa.push_str("_");
println!("aa: {:?}", aa)
}
Now let's say that I have two Option<String> values that I want to pattern match over.
let _b: Option<String> = Some(String::from("bar"));
let _c: Option<String> = Some(String::from("baz"));
if let (Some(ref mut bb), Some(ref mut cc)) = (_b, _c) {
bb.push_str("_");
cc.push_str("_");
println!("bb: {:?}, cc: {:?}", bb, cc);
}
Strangely, I can use ref mut in the patterns, even though neither _b nor _c are mutable, and I can mutate the strings. Why is that allowed here in this case? I'd expect this not to compile unless both _b and _c are declared as mutable similar to the first example above.
I think what may be happening is that a tuple is constructed in the pattern match, i.e. (_b, _c), and then some compiler magic allows the ref mut on the pattern that is "bound" to this tuple. Is that correct?
Rust version:
rustc 1.41.1 (f3e1a954d 2020-02-24)
The mut isn't necessary if the value is wrapped in a tuple, because the tuple is a new value (playground):
let a = Some(String::from("foo"));
if let (Some(mut aa),) = (a,) {
aa.push_str("_");
println!("aa: {:?}", aa)
}
I'm trying to join strings in a vector into a single string, in reverse from their order in the vector. The following works:
let v = vec!["a".to_string(), "b".to_string(), "c".to_string()];
v.iter().rev().map(|s| s.clone()).collect::<Vec<String>>().connect(".")
However, this ends up creating a temporary vector that I don't actually need. Is it possible to do this without a collect? I see that connect is a StrVector method. Is there nothing for raw iterators?
I believe this is the shortest you can get:
fn main() {
let v = vec!["a".to_string(), "b".to_string(), "c".to_string()];
let mut r = v.iter()
.rev()
.fold(String::new(), |r, c| r + c.as_str() + ".");
r.pop();
println!("{}", r);
}
The addition operation on String takes its left operand by value and pushes the second operand in-place, which is very nice - it does not cause any reallocations. You don't even need to clone() the contained strings.
I think, however, that the lack of concat()/connect() methods on iterators is a serious drawback. It bit me a lot too.
I don't know if they've heard our Stack Overflow prayers or what, but the itertools crate happens to have just the method you need - join.
With it, your example might be laid out as follows:
use itertools::Itertools;
let v = ["a", "b", "c"];
let connected = v.iter().rev().join(".");
Here's an iterator extension trait that I whipped up, just for you!
pub trait InterleaveExt: Iterator + Sized {
fn interleave(self, value: Self::Item) -> Interleave<Self> {
Interleave {
iter: self.peekable(),
value: value,
me_next: false,
}
}
}
impl<I: Iterator> InterleaveExt for I {}
pub struct Interleave<I>
where
I: Iterator,
{
iter: std::iter::Peekable<I>,
value: I::Item,
me_next: bool,
}
impl<I> Iterator for Interleave<I>
where
I: Iterator,
I::Item: Clone,
{
type Item = I::Item;
#[inline]
fn next(&mut self) -> Option<Self::Item> {
// Don't return a value if there's no next item
if let None = self.iter.peek() {
return None;
}
let next = if self.me_next {
Some(self.value.clone())
} else {
self.iter.next()
};
self.me_next = !self.me_next;
next
}
}
It can be called like so:
fn main() {
let a = &["a", "b", "c"];
let s: String = a.iter().cloned().rev().interleave(".").collect();
println!("{}", s);
let v = vec!["a".to_string(), "b".to_string(), "c".to_string()];
let s: String = v.iter().map(|s| s.as_str()).rev().interleave(".").collect();
println!("{}", s);
}
I've since learned that this iterator adapter already exists in itertools under the name intersperse — go use that instead!.
Cheating answer
You never said you needed the original vector after this, so we can reverse it in place and just use join...
let mut v = vec!["a".to_string(), "b".to_string(), "c".to_string()];
v.reverse();
println!("{}", v.join("."))