How can I use rust Try trait with Option NoneError? - error-handling

I've written a custom protocol where I've defined my own struct for a frame and it parses from bytes. My function accepts a Vec and parses the elements accordingly. To account for invalid frames, I am returning a Result<Frame> and calling .get() on the byte array. Here's my code:
fn main(){
let emptyvec = Vec::new();
match Frame::from_bytes(emptyvec) {
Err(e) => {
println!("Received invalid frame");
},
Ok(frame) => {
println!("Received valid frame");
}
}
}
struct Frame {
txflag: u8, // indicates if chunked
msgtype: u8, // a flag for message type
sender: u8, // which node ID sent this frame?
routeoffset: u8, // size of array of route for frame
route: Vec<u8>, // a list of node IDs that frame should pass
payload: Vec<u8>, // payload data
}
impl Frame {
/// parse from raw bytes
pub fn from_bytes(bytes: &Vec<u8>) -> std::io::Result<Self> {
let txflag = bytes.get(0)?.clone();
let msgtype = bytes.get(1)?.clone();
let sender = bytes.get(2)?.clone();
let routesoffset = bytes.get(3)?.clone();
let routes = &bytes.get(4..(4+routesoffset as usize))?;
let (left, right) = bytes.split_at(2);
let data = Vec::from(right);
Ok(Frame {
txflag,
msgtype,
sender,
routeoffset: routesoffset,
route: Vec::from(routes),
payload: data
})
}
}
However when I try to use this pattern I get the following compilation error, and when attempting to implement the trait I get an error that the Try trait is unstable.
error[E0277]: `?` couldn't convert the error to `std::io::Error`
--> src/stack/frame.rs:121:34
|
121 | let txflag = bytes.get(0)?.clone();
| ^ the trait `std::convert::From<std::option::NoneError>` is not implemented for `std::io::Error`
Not quite sure how to proceed but I'd like to use stable features to solve this. The goal here is to be able to parse bytes and handle an invalid frame as necessary.

This is probably what you want
use std::io::{Error, ErrorKind};
fn main() {
let emptyvec = Vec::new();
match Frame::from_bytes(&emptyvec) {
Err(e) => {
println!("Received invalid frame");
}
Ok(frame) => {
println!("Received valid frame");
}
}
}
struct Frame {
txflag: u8,
// indicates if chunked
msgtype: u8,
// a flag for message type
sender: u8,
// which node ID sent this frame?
routeoffset: u8,
// size of array of route for frame
route: Vec<u8>,
// a list of node IDs that frame should pass
payload: Vec<u8>, // payload data
}
impl Frame {
/// parse from raw bytes
pub fn from_bytes(bytes: &Vec<u8>) -> std::io::Result<Self> {
let txflag = bytes.get(0).ok_or(Error::from(ErrorKind::InvalidData))?.clone();
let msgtype = bytes.get(1).ok_or(Error::from(ErrorKind::InvalidData))?.clone();
let sender = bytes.get(2).ok_or(Error::from(ErrorKind::InvalidData))?.clone();
let routesoffset = bytes.get(3).ok_or(Error::from(ErrorKind::InvalidData))?.clone();
let routes = bytes
.get(4..(4 + routesoffset as usize))
.ok_or(Error::from(ErrorKind::InvalidData))?
.clone();
let (_, right) = bytes.split_at(2);
let data = Vec::from(right);
Ok(Frame {
txflag,
msgtype,
sender,
routeoffset: routesoffset,
route: Vec::from(routes),
payload: data,
})
}
}
Here is Rust Playground
You are trying to call ? on Option. You have to convert Option to Result (If you still want to use ?).

I want to add to what Đorðe Zeljić said:
As he already pointed out the result of bytes.get(0) is a std::option::Option. When you use the ? operator on that you already left the grounds of stable Rust. This application is only supported in unstable Rust at the moment.
If you want to stay in stable Rust, it's probably best to do what Đorðe wrote. If you want to keep using the ? operator because it produces nicer looking code, here is what's going on:
Rust has a lot of error types, each being only able to represent what they are made for. If you are using a std::io::Result this implicitly uses the error type std::io::Error which is only able to represent typical I/O errors. This type is not able to represent “there was no value when I expected one”. That's why from applying ? to a Option with the None value, you don't get a std::io::Error but a different kind of error: std::option::NoneError.
When your Rust application grows it will happen often, that you have to return a Result that can contain different types of errors. In that case you normally define your own error type (enum), that can represent different kinds of errors. Then for each error, that can be contained, you have to define the From trait on your own enum. This can be a lot of repeated work, so there is a macro in the quick-error crate, that helps with that and implements the From trait automatically for each error that can be contained.
To get your code compiling, you could define the following error enum, that can represent std::io::Error as well as std::option::NoneError:
quick_error! {
#[derive(Debug)]
pub enum FrameError {
IoError(err: std::io::Error) {from() cause(err)}
MissingValue(err: std::option::NoneError) {from()}
}
}
Instead of std::io::Result<Self> your from_bytes function then has to return a std::result::Result that uses your new error type: Result<Self, FrameError>.
Completely assembled that looks like this:
#![feature(try_trait)]
use quick_error::*;
quick_error! {
#[derive(Debug)]
pub enum FrameError {
IoError(err: std::io::Error) {from() cause(err)}
MissingValue(err: std::option::NoneError) {from()}
}
}
fn main() {
let emptyvec = Vec::new();
match Frame::from_bytes(&emptyvec) {
Err(_e) => {
println!("Received invalid frame");
}
Ok(_frame) => {
println!("Received valid frame");
}
}
}
struct Frame {
txflag: u8, // indicates if chunked
msgtype: u8, // a flag for message type
sender: u8, // which node ID sent this frame?
routeoffset: u8, // size of array of route for frame
route: Vec<u8>, // a list of node IDs that frame should pass
payload: Vec<u8>, // payload data
}
impl Frame {
/// parse from raw bytes
pub fn from_bytes(bytes: &Vec<u8>) -> Result<Self, FrameError> {
let txflag = bytes.get(0)?.clone();
let msgtype = bytes.get(1)?.clone();
let sender = bytes.get(2)?.clone();
let routesoffset = bytes.get(3)?.clone();
let routes = bytes.get(4..(4 + routesoffset as usize))?;
let (left, right) = bytes.split_at(2);
let data = Vec::from(right);
Ok(Frame {
txflag,
msgtype,
sender,
routeoffset: routesoffset,
route: Vec::from(routes),
payload: data,
})
}
}
To use the quick-error crate, you have to add the following to your Cargo.toml:
[dependencies]
quick-error = "1.2.3"

Related

How to index struct values of different types

I'm new to Rust, probably missing something obvious. I have the following code with the main idea of being able to index any struct field like so struct_instance['field'].
use std::ops::Index;
enum Selection {
Full,
Partial,
}
struct Config {
display: bool,
timeout: u16,
selection: Selection,
}
impl Index<&'_ str> for Config {
type Output = bool;
fn index(&self, index: &'_ str) -> &Self::Output {
match index {
"display" => &self.display,
_ => panic!("Unknown field: {}", index),
}
}
}
fn main() {
let config = Config {
display: true,
timeout: 500,
selection: Selection::Partial,
};
let display = config["display"];
println!("{display}");
}
The problem is: I can not find a way to index every type of struct fields, because associated type Output doesn't let me define more than one type. I would want to have match being able to process all Config fields somehow, is there a way to do so?
As answered apilat , Index is for array like structures.
However if you want, you can achieve this with enums.
Create enum with all available types of config fields (bool, u16, Selection, etc...)
Change Config fields' types to this new enum
Change the Output in the Index impl again to this new enum
Here is full code example
use std::ops::Index;
#[derive(Debug)]
enum ConfigField {
Display(bool),
Timeout(u16),
Selection(Selection)
}
#[derive(Debug)]
enum Selection {
Full,
Partial,
}
struct Config {
display: ConfigField,
timeout: ConfigField,
selection: ConfigField,
}
impl Index<&'_ str> for Config {
type Output = ConfigField;
fn index(&self, index: &'_ str) -> &Self::Output {
match index {
"display" => &self.display,
"timeout" => &self.timeout,
"selection" => &self.selection,
_ => panic!("Unknown field: {}", index),
}
}
}
fn main() {
let config = Config {
display: ConfigField::Display(true),
timeout: ConfigField::Timeout(500),
selection: ConfigField::Selection(Selection::Partial),
};
let display = &config["display"];
let timeout = &config["timeout"];
let selection = &config["selection"];
println!("{:?} {:?} {:?}", display, timeout, selection);
}

Failed to fill whole buffer with rust read_exact

I have a little code snippet where I'm trying to write struct to a file and then read it. I have seen other similar posts where the asker has forgotten to zero initialise the buffer they are trying to read into. I have made sure not to do this, but I still am getting the error that 'failed to fill whole buffer' error when using read_exact, even though my buffer size and the size of the file I'm trying to read are the same.
Here is the code:
use std::fs::{File, OpenOptions};
use std::io::prelude::*;
use bincode::*;
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize, PartialEq, Debug)]
pub struct Node {
pub name: String,
end_ptr: u32 // number of bytes away the next node is
}
impl Node {
pub fn to_string(&self) -> String {
return self.name.clone();
}
}
fn main() {
let node = Node { name: String::from("node_1"), end_ptr: 0 };
let node_as_buf = bincode::serialize(&node).unwrap();
let len_of_bytes_serialised: usize = node_as_buf.len();
let mut file = OpenOptions::new()
.read(true)
.write(true)
.create(true)
.truncate(true)
.open("test.txt")
.unwrap();
match file.write_all(&node_as_buf) {
Ok(result) => {println!("Write success")},
Err(err) => {println!("{}", &err)}
}
println!("{}", file.metadata().unwrap().len()); // this and
println!("{}", len_of_bytes_serialised); // this are the same size
let mut buffer = vec![0; len_of_bytes_serialised];
match file.read_exact(&mut buffer[..]) {
Ok(result) => println!("Read success"),
Err(err) => println!("{}", &err) // prints 'failed to fill whole buffer'
}
let read_node: Node = bincode::deserialize(&buffer[..]).unwrap();
}
Because you use the same stream for reading and writing, the cursor is moved to the end of the file and any reading will attempt to read there, which will of course fail. You can observe that if you'll print file.stream_position().
You need to rewind() before reading.

Idiomatic way to collect all errors from an iterator

Let's say I have a attrs: Vec<Attribute> of some function attributes and a function fn map_attribute(attr: &Attribute) -> Result<TokenStream, Error> that maps the attributes to some code.
I know that I could write something like this:
attrs.into_iter()
.map(map_attribute)
.collect::<Result<Vec<_>, _>()?
However, this is not what I want. What I want is spit out all errors at once, not stop with the first Error. Currently I do something like this:
let mut codes : Vec<TokenStream> = Vec::new();
let mut errors: Vec<Error> = Vec::new();
for attr in attrs {
match map_attribute(attr) {
Ok(code) => codes.push(code),
Err(err) => errors.push(err)
}
}
let mut error_iter = errors.into_iter();
if let Some(first) = error_iter.nth(0) {
return Err(iter.fold(first, |mut e0, e1| { e0.combine(e1); e0 }));
}
This second version does what I want, but is considerably more verbose than the first version. Is there a better / more idiomatic way to acchieve this, if possible without creating my own iterator?
The standard library does not have a convenient one-liner for this as far as I know, however the excellent itertools library does:
use itertools::Itertools; // 0.9.0
fn main() {
let foo = vec![Ok(42), Err(":("), Ok(321), Err("oh noes")];
let (codes, errors): (Vec<_>, Vec<_>)
= foo.into_iter().partition_map(From::from);
println!("codes={:?}", codes);
println!("errors={:?}", errors);
}
(Permalink to the playground)
I ended up writing my own extension for Iterator, which allows me to stop collecting codes when I encounter my first error. This is in my use case probably a bit more efficient than the answer by mcarton, since I only need the first partition bucket if the second one is empty. Also, I need to fold the errors anyways if I want to combine them into a single error.
pub trait CollectToResult
{
type Item;
fn collect_to_result(self) -> Result<Vec<Self::Item>, Error>;
}
impl<Item, I> CollectToResult for I
where
I : Iterator<Item = Result<Item, Error>>
{
type Item = Item;
fn collect_to_result(self) -> Result<Vec<Item>, Error>
{
self.fold(<Result<Vec<Item>, Error>>::Ok(Vec::new()), |res, code| {
match (code, res) {
(Ok(code), Ok(mut codes)) => { codes.push(code); Ok(codes) },
(Ok(_), Err(errors)) => Err(errors),
(Err(err), Ok(_)) => Err(err),
(Err(err), Err(mut errors)) => { errors.combine(err); Err(errors) }
}
})
}
}

How to retrieve the underlying error from a Failure Error?

When trying to open a broken epub/ZIP file with epub-rs, the zip-rs crate error (which doesn't use Failure) is wrapped into a failure::Error by epub-rs. I want to handle each error type of zip-rs with an distinct error handler and need a way to match against the underlying error. How can I retrieve it from Failure?
fn main() {
match epub::doc::EpubDoc::new("a.epub") {
Ok(epub) => // do something with the epub
Err(error) => {
// handle errors
}
}
}
error.downcast::<zip::result::ZipError>() fails and error.downcast_ref() returns None.
You can downcast from a Failure Error into another type that implements Fail by using one of three functions:
downcast
downcast_ref
downcast_mut
use failure; // 0.1.5
use std::{fs, io};
fn generate() -> Result<(), failure::Error> {
fs::read_to_string("/this/does/not/exist")?;
Ok(())
}
fn main() {
match generate() {
Ok(_) => panic!("Should have an error"),
Err(e) => match e.downcast_ref::<io::Error>() {
Some(e) => println!("Got an io::Error: {}", e),
None => panic!("Could not downcast"),
},
}
}
For your specific case, I'm guessing that you are either running into mismatched dependency versions (see Why is a trait not implemented for a type that clearly has it implemented? for examples and techniques on how to track this down) or that you simply are getting the wrong error type. For example, a missing file is actually an std::io::Error:
// epub = "1.2.0"
// zip = "0.4.2"
// failure = "0.1.5"
use std::io;
fn main() {
if let Err(error) = epub::doc::EpubDoc::new("a.epub") {
match error.downcast_ref::<io::Error>() {
Some(i) => println!("IO error: {}", i),
None => {
panic!("Other error: {} {:?}", error, error);
}
}
}
}

Is there a more ergonomic syntax for Either when using futures?

Here's an example of using Tokio to run a function that returns a future:
use futures::sync::oneshot;
use futures::Future;
use std::thread;
use std::time::Duration;
use tokio;
#[derive(Debug)]
struct MyError {
error_code: i32,
}
impl From<oneshot::Canceled> for MyError {
fn from(_: oneshot::Canceled) -> MyError {
MyError { error_code: 1 }
}
}
fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
let (sx, rx) = oneshot::channel();
thread::spawn(move || {
thread::sleep(Duration::from_millis(100));
sx.send(100).unwrap();
});
return rx.map_err(|e| MyError::from(e));
}
fn main() {
tokio::run(deferred_task().then(|r| {
println!("{:?}", r);
Ok(())
}));
}
However, when the function in question (i.e. deferred_task) is non-trivial, the code becomes much more complex when I write it, because the ? operation doesn't seem to easily mix with returning a future:
fn send_promise_to_worker(sx: oneshot::Sender<i32>) -> Result<(), ()> {
// Send the oneshot somewhere in a way that might fail, eg. over a channel
thread::spawn(move || {
thread::sleep(Duration::from_millis(100));
sx.send(100).unwrap();
});
Ok(())
}
fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
let (sx, rx) = oneshot::channel();
send_promise_to_worker(sx)?; // <-------- Can't do this, because the return is not a result
return rx.map_err(|e| MyError::from(e));
}
A Future is a Result, it's meaningless to wrap it in result, and it breaks the impl Future return type.
Instead you get a deeply nested chain of:
fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
let (sx, rx) = oneshot::channel();
match query_data() {
Ok(_i) => match send_promise_to_worker(sx) {
Ok(_) => Either::A(rx.map_err(|e| MyError::from(e))),
Err(_e) => Either::B(futures::failed(MyError { error_code: 2 })),
},
Err(_) => Either::B(futures::failed(MyError { error_code: 2 })),
}
}
full code
The more results you have, the deeper the nesting; exactly what the ? operator solves normally.
Am I missing something? Is there some syntax sugar to make this easier?
I do not see how async / await syntax will categorically help you with Either. Ultimately, you still need to return a single concrete type, and that's what Either provides. async / await will reduce the need for combinators like Future::map or Future::and_then however.
See also:
Why can impl trait not be used to return multiple / conditional types?
That being said, you don't need to use Either here.
You have consecutive Result-returning functions, so you can borrow a trick from JavaScript and use an IIFE to use use the ? operator. Then, we can "lift up" the combined Result into a future and chain it with the future from the receiver:
fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
let (tx, rx) = oneshot::channel();
let x = (|| {
let _i = query_data().map_err(|_| MyError { error_code: 1 })?;
send_promise_to_worker(tx).map_err(|_| MyError { error_code: 2 })?;
Ok(())
})();
future::result(x).and_then(|()| rx.map_err(MyError::from))
}
In the future, that IIFE could be replaced with a try block, as I understand it.
You could also go the other way and convert everything to a future:
fn deferred_task() -> impl Future<Item = i32, Error = MyError> {
let (tx, rx) = oneshot::channel();
query_data()
.map_err(|_| MyError { error_code: 1 })
.into_future()
.and_then(|_i| {
send_promise_to_worker(tx)
.map_err(|_| MyError { error_code: 2 })
.into_future()
})
.and_then(|_| rx.map_err(MyError::from))
}
This would be helped with async / await syntax:
async fn deferred_task() -> Result<i32, MyError> {
let (tx, rx) = oneshot::channel();
query_data().map_err(|_| MyError { error_code: 1 })?;
send_promise_to_worker(tx).map_err(|_| MyError { error_code: 2 })?;
let v = await! { rx }?;
Ok(v)
}
I have also seen improved syntax for constructing the Either by adding left and right methods to the Future trait:
foo.left();
// vs
Either::left(foo);
However, this doesn't appear in any of the current implementations.
A Future is a Result
No, it is not.
There are two relevant Futures to talk about:
From the futures 0.1 crate
From the (nightly) standard library
Notably, Future::poll returns a type that can be in two states:
Complete
Not complete
In the futures crate, "success" and "failure" are tied to "complete", whereas in the standard library they are not. In the crate, Result implements IntoFuture, and in the standard library you can use future::ready. Both of these allow converting a Result into a future, but that doesn't mean that Result is a future, no more than saying that a Vec<u8> is an iterator, even though it can be converted into one.
It's possible that the ? operator (powered by the Try trait), will be enhanced to automatically convert from a Result to a specific type of Future, or that Result will even implement Future directly, but I have not heard of any such plans.
Is there some syntax sugar to make this easier?
Yes, it's called async/await, but it's not quite ready for wide consumption. It is only supported on nightly, it uses a slightly different version of futures that Tokio only supports via an interop library that causes additional syntactic overhead, and documentation for the whole thing is still spotty.
Here are some relevant links:
What is the purpose of async/await in Rust?
https://jsdw.me/posts/rust-asyncawait-preview/
https://areweasyncyet.rs/