Rust chaining Results (Combinators) - rust

I've been following a Rust tutorial where two versions of a function are purported to generate the same results:
Version 1:
pub fn get_transactions(fname:&str) -> Result<Vec<Transaction>,String> {
let s = match std::fs::read_to_string(fname){
Ok(v)=>v,
Err(e)=> return Err(e.to_string()),
};
let t:Vec<Transaction> = match serde_json::from_str(&s) {
Ok(v)=>v,
Err(e)=> return Err(e.to_string()),
};
Ok(t)
}
Version 2:
fn get_transactions_b(fname:&str) -> Result<Vec<Transaction>,String> {
std::fs::read_to_string(fname)
.map_err(|e| e.to_string())
.and_then(|ld| serde_json::from_str(&ld) )
.map_err(|e| e.to_string())
}
However, I get the following message for version 2:
mismatched types
expected struct std::string::String, found struct serde_json::error::Error
note: expected enum std::result::Result<_, std::string::String>
found enum std::result::Result<_, serde_json::error::Error>
help: try using a variant of the expected enum: _::_serde::export::Ok(serde_json::from_str(&ld)),
which I been unable to make head or tail out of:
Hovering over ld closure argument |ld| in VS Code it says it's of type std::string::String
Hovering over the ld in the from_str(&ld) I get the message.
Now I understand a Result is comprised of an Ok and an Err, but I thought the combinator chaining would have worked.
The compiler suggested fix doesn't work either.
(or make sense): What is the _::_ all about?
Why would the Ok not be inside the from_str?
What would you have to do to make version 2 work?
Here's the Result with the methods for combinators in the Rust docs.
Cargo.toml
[dependencies]
serde = "1.0.115"
serde_derive = "1.0.115"
serde_json = "1.0.57"
cargo 1.45.1
rustc 1.45.2

The problem comes from the and_then call.
You are not allowed to change the error type when calling and_then.
So your function there should return a Result<Vec<Transaction>, String>. However, the error type returned by serde_json::from_str is a serde_json::error::Error.
You can fix it like this:
std::fs::read_to_string(fname)
.map_err(|e| e.to_string())
.and_then(|ld| serde_json::from_str(&ld).map_err(|e| e.to_string()))
Notice the call to map_err is now inside the function passed to and_then.

Related

Using assert in error branch when running cargo test

I'm reading through the Rust book and working on part of the minigrep where it asks you to write some unit tests on the Config::new function. I'm failing on something that I didn't expect and don't understand why (Google-fu is failing me as well).
This fails
#[cfg(test)]
mod new_config_tests {
use super::*;
#[test]
fn it_can_create_a_new_config() {
let expected_query = "expected_qury";
let expected_filename = "expected_filename.txt";
let args: Vec<String> = vec!["program/path".to_string(), expected_query.to_string(), expected_filename.to_string()];
// failing line
let actual = Config::new(&args).unwrap_or_else(|err| { assert!(false); });
}
}
impl Config {
pub fn new(args: &[String]) -> Result<Config, &'static str> {
if args.len() < 3 {
return Err("not enough arguments\n");
}
let query = args[1].clone();
let filename = args[2].clone();
Ok(Config { query, filename })
}
}
with
error[E0308]: mismatched types
--> src/lib.rs:19:62
|
19 | let actual = Config::new(&args).unwrap_or_else(|err| { assert!(false); });
| ^^^^^^^^^^^^^^^^^^^ expected struct `Config`, found `()`
In this test, I'm just making sure that I can create a new Config and want it to fail if the Config::new function fails. I thought that using assert would be correct so that the test framework would handle the failure. If I change the assert to panic, then the tests pass as expected. Is using panic correct in the above scenario?
The problem is that during type-checking, the compiler doesn't (yet) realize that assert!(false) will always fail, so it has to assume that it may pass, resulting in a value of type () which is incompatible with the expected Config type.
Conversely, if you replace the assert with a call to panic, the compiler knows that panic never returns, so it doesn't matter if there is no Config to return. (Strictly speaking, panic typechecks as returning a value of type !, aka the "never" type, which is compatible with everything).
IMO you should never use assert!(false), and instead use panic when you know that a condition is always fatal. This makes your intent clearer.
Although in this specific case, it would probably be better to assert that the result is Ok:
assert!(Config::new(&args).is_ok());

Creating a cyclic Tokio stream connected to a shared state

I am running into a problem that I do not really understand and hoped
that somebody might be able to see what I have misunderstood.
The problem is quite straightforward: I have a global state (shared
between several tasks) and want to have an infinite cycle over a
vector in the global state. I will then zip that with an interval
stream and hence get a regular emission of the next value in the
stream.
If the vector in the state changes, the inifinite stream should just
reload the vector and start reading from the new one instead, and
discard the old array.
Here is the code that I've gotten this far, and the questions are at
the end of the post.
use futures::stream::Stream;
use futures::{Async, Poll};
use std::iter::Cycle;
use std::sync::{Arc, Mutex};
use std::time::{Duration, Instant};
use tokio::timer::Interval;
We define a global state that hold an array that can be
updated. Whenever the array is updated, we will step the version and
set the array.
struct State<T> {
version: u32,
array: Vec<T>,
}
impl<T> State<T> {
fn new(array: Vec<T>) -> Self {
Self {
version: 0,
array: Vec::new(),
}
}
fn update(&mut self, array: Vec<T>) {
self.version += 1;
self.array = array;
}
}
Now, we create an stream over the state. When initialized, it will
read the array and version from the state and store it and then keep
an instance of std::iter::Cycle internally that will cycle over the
array.
struct StateStream<I> {
state: Arc<Mutex<State<I::Item>>>,
version: u32,
iter: Cycle<I>,
}
impl<I> StateStream<I>
where
I: Iterator,
{
fn new(state: Arc<Mutex<State<I::Item>>>) -> Self {
let (version, array) = {
let locked_state = state.lock().unwrap();
(locked_state.version, locked_state.array)
};
Self {
state: state,
version: version,
iter: array.iter().cycle(),
}
}
}
We now implement the stream for the StateStream. With each poll, it
will check if the version of the state changed, and if it did, reload
the array and version.
We will then take the next item from the iterator and return that.
impl<I> Stream for StateStream<I>
where
I: Iterator + Clone,
{
type Item = I::Item;
type Error = ();
fn poll(&mut self) -> Poll<Option<Self::Item>, Self::Error> {
let locked_state = self.state.lock().unwrap();
if locked_state.version > self.version {
self.iter = locked_state.array.clone().iter().cycle();
self.version = locked_state.version;
}
Ok(Async::Ready(self.iter.next()))
}
}
The main program looks like this. I do not update the vector here, but
that is not important for the case at hand.
fn main() {
let state = Arc::new(Mutex::new(State::new(vec![2, 3, 5, 7, 11, 13])));
let primes = StateStream::new(state)
.take(20)
.zip(
Interval::new(Instant::now(), Duration::from_millis(500))
.map_err(|err| println!("Error: {}", err)),
)
.for_each(|(number, instant)| {
println!("fire; number={}, instant={:?}", number, instant);
Ok(())
});
tokio::run(primes);
}
When compiling this, I get the following errors:
cargo run --example cycle_stream_shared
Compiling tokio-testing v0.1.0 (/home/mats/crates/tokio-examples)
error[E0308]: mismatched types
--> examples/cycle_stream_shared.rs:66:19
|
66 | iter: array.iter().cycle(),
| ^^^^^^^^^^^^^^^^^^^^ expected type parameter, found struct `std::slice::Iter`
|
= note: expected type `std::iter::Cycle<I>`
found type `std::iter::Cycle<std::slice::Iter<'_, <I as std::iter::Iterator>::Item>>`
error[E0308]: mismatched types
--> examples/cycle_stream_shared.rs:81:25
|
81 | self.iter = locked_state.array.clone().iter().cycle();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected type parameter, found struct `std::slice::Iter`
|
= note: expected type `std::iter::Cycle<I>`
found type `std::iter::Cycle<std::slice::Iter<'_, <I as std::iter::Iterator>::Item>>`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.
error: Could not compile `tokio-testing`.
To learn more, run the command again with --verbose.
Now, the error and the explanation says that the concrete type is not
possible to derive, but in this case, I am using the generic struct
Cycle<I> and expect I to be instantiated to std::slice::Iter<'_,
I::Item>. Since std::slice::Iter has implemented Iterator and, the type have implemented all necessary traits to match.
Some answers to similar questions exist, but nothing that seems to
match this case:
“Expected type parameter” error in the constructor of a generic
struct is showing that the types do not match (same
as the explanation gives) because the generic struct definition allow any type, but the construction require a specific type.
In this case, we are using a generic type Cycle<I>, where I should implement the Iterator trait, and try to use a type std::slice::Iter<..> that does implement Iterator.
How do I return an instance of a trait from a
method? talk about how to return an arbitrary type
matching a trait, which is not the case here.
The other questions are mostly referring to these two, or variations
of these.
Update: Changed it to be a generic type to demonstrate that it still does not work.

Calling map on an Option<Rc<Struct>> works differently than calling it on a Option<Rc<i32>>

I'm new to Rust and I'm trying to figure out why Rc is behaving differently when being passed to a closure. My full code is the following:
use std::rc::Rc;
struct Something {
value: i32
}
fn main() {
let wrapped_struct = Some(Rc::new(Something { value: 1 }));
let wrapped_integer = Some(Rc::new(1));
// Case 1: This works
let works: Option<i32> = wrapped_struct.map(|i| { i.value });
// Case 2: This fails
let fails: Option<i32> = wrapped_integer.map(|i| { i });
}
The error message is:
|
13 | let fails: Option<i32> = wrapped_integer.map(|i| { i });
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected i32, found struct `std::rc::Rc`
|
= note: expected type `std::option::Option<i32>`
found type `std::option::Option<std::rc::Rc<{integer}>>`
What I don't understand is why in the first closure (Case 1) I can use i as a Something (I'd expect Rc<Something>) but in the second one (Case 2) I can't use i as an i32 (I actually get an Rc<i32>).
I appreciate any pointers to the relevant documentation. Thanks a lot!
The type of the i in both closures is actually Rc<Something> and Rc<i32> respectively. Rc can be dereferenced to access its inner data, but there are places in Rust where dereferencing happens automatically, for convenience.
In the struct case, when you write i.value, it will automatically dereference i to access the field. It then returns a copy of the i32, because i32 is a Copy type. So the type of the expression i.value is i32. It's as if you wrote (*i).value, but Rust did the dereferencing for you.
In the i32 case, you are just returning the i, which still has type Rc<i32>. You can fix it by explicitly dereferencing:
wrapped_integer.map(|i| { *i });
See also:
What are Rust's exact auto-dereferencing rules?

Err doesn't accept argument that has been converted "to_string"

I am trying to return an error with a formatted message.
let msg = format!("wrong num of dimensions: {}", dimensions.len()).to_string();
return Err(msg);
dimensions is a Vec<i32> that is supposed to have a length of 3. If it doesn't, I want to raise this error. However, I am getting this compiler error:
Compiling advent02 v0.1.0 (file:///home/garrett/notes/rust/advent/advent02)
src/main.rs:19:24: 19:27 error: mismatched types:
expected `&str`,
found `collections::string::String`
(expected &-ptr,
found struct `collections::string::String`) [E0308]
src/main.rs:19 return Err(msg);
^~~
It looks to me like it wants an &str type when I am giving it a String type. I have tried to do this from this example but get borrow errors because the returned string borrow goes out of scope.
I don't understand because this page is littered with examples that do Err(err.to_string()), and they don't seem to have a problem.
The return type of your function is Result<T, &str>, that's why the compiler is complaining. You create a String instance, take a reference to it and try to return. When the function returns, the String instance is freed and your returned reference would be invalid (a dangling pointer).
I'd change the signature of the function to return a Result<T, String>. That way, you move the string out of the function without freeing it.
Here is a complete Rust program that shows the behavior you wanted:
fn test() -> Result<u32, String> {
let msg = format!("Error code {}.", 123).to_string();
return Err(msg);
}
fn main() {
match test() {
Ok(num) => println!("Okay: {}", num),
Err(msg) => println!("Error: {}", msg),
};
}
This compiles without warnings or errors and prints "Error: Error code 123." when run.
The important thing here is to declare the return type of the function that is returning the error code. If you don't declare the return type, then the Rust compiler apparently does a bad job of figuring out what the return type should be, and you get errors (but the errors I get from rustc 1.5.0 are different from yours). This might actually be a bug in the Rust compiler; you could report this to the developers of the language and see what they have to say.
The Err thing that you used is just a type constructor for std::result::Result. So the return type of the function should be some type of Result.

How to most generically iterate a sequence of items with a given type?

The code below best describes the issue.
use std::iter::IntoIterator;
fn iterate<I: IntoIterator<Item=String>>(v: I) {
}
// iterate(&["foo".to_string()])
// error: type mismatch resolving `<&[collections::string::String; 1] as core::iter::IntoIterator>::Item == collections::string::String`:
// expected &-ptr,
// found struct `collections::string::String` [E0271]
// iterate(["foo".to_string()].iter())
// type mismatch resolving `<core::slice::Iter<'_, collections::string::String> as core::iter::IntoIterator>::Item == collections::string::String`:
// expected &-ptr,
// found struct `collections::string::String` [E0271]
// This works !
iterate(vec!["foo".to_string()])
How can I iterate anything (with a given item type) generically?
Additional Notes
The intention is to allow users of such a function to pass in anything that can be iterated, or converted into an iterator.
Also I have the feeling that the actual issue is not really described in the compiler error - as the type it sees seems to be different from what it shows.
I am using rustc 1.0.0-nightly (522d09dfe 2015-02-19) (built 2015-02-19)
Let's look at what the types are for your first case:
for i in &["foo".to_string()] {
let () = i;
// expected `&collections::string::String`,
// found `()`
}
That is, the type of your iteration variable is &String, not on String, as your function wants. The same thing happens for your second case. The third case works:
for i in vec!["foo".to_string()] {
let () = i;
// expected `collections::string::String`,
// found `()`
}
We can look at the implementation for IntoIter for arrays and for all 3 forms of Vec. Note that the implementation for Vec<T> consumes the vector, whereas the one for &Vec<T> must return an iterator over references to the elements.
You can't consume an array, so that iterator must always return references.
Here's an example that uses the AsRef trait that should do what you want:
use std::iter::IntoIterator;
fn iterate<I, S>(v: I)
where I: IntoIterator<Item=S>,
S: AsRef<str>
{}
fn main() {
iterate(&["foo"]);
iterate(&["foo".to_string()]);
iterate(["foo".to_string()].iter());
iterate(vec!["foo".to_string()]);
iterate(&vec!["foo".to_string()]);
}
This says that we expect any concrete type that implements the trait Iterator. That iterator must yield a type that implements the trait AsRef<str>, allowing us to pass in {arrays,vectors,slices} of {String,&str}.

Resources