Как я могу использовать rust Try trait с Option NoneError? - PullRequest
0 голосов
/ 06 апреля 2020

Я написал собственный протокол, в котором я определил свою собственную структуру для фрейма, и он анализирует байты. Моя функция принимает Vec и анализирует элементы соответственно. Чтобы учесть недопустимые кадры, я возвращаю Result<Frame> и вызываю .get() в байтовом массиве. Вот мой код:

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
        })
    }
}

Однако, когда я пытаюсь использовать этот шаблон, я получаю следующую ошибку компиляции, и при попытке реализовать черту я получаю ошибку, что черта Try нестабильна.

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`

Не совсем уверен, как действовать, но я хотел бы использовать стабильные функции для решения этой проблемы. Цель здесь состоит в том, чтобы иметь возможность анализировать байты и обрабатывать недопустимый кадр при необходимости.

Ответы [ 2 ]

4 голосов
/ 06 апреля 2020

Это, вероятно, то, что вы хотите

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,
        })
    }
}

Вот Rust Playground

Вы пытаетесь вызвать ? на Option. Вы должны конвертировать Option в Result (если вы все еще хотите использовать ?).

1 голос
/ 06 апреля 2020

Я хочу добавить к тому, что сказал Жорде Зельич:

Как он уже указывал, результат bytes.get(0) равен std::option::Option. При использовании оператора ? на этом вы уже оставили основания стабильного Rust. На данный момент это приложение поддерживается только в нестабильном Rust.

Если вы хотите остаться в стабильном Rust, лучше всего делать то, что написал ðorðe. Если вы хотите продолжать использовать оператор ?, потому что он производит более красивый код, вот что происходит:

Rust имеет много типов ошибок, каждый из которых может представлять только то, для чего они созданы. Если вы используете std::io::Result, это неявно использует тип ошибки std::io::Error, который способен отображать только типичные ошибки ввода-вывода. Этот тип не может представлять «не было значения, когда я ожидал его». Вот почему при применении ? к Option со значением None вы не получите std::io::Error, но ошибка другого типа: std::option::NoneError.

Когда ваше приложение Rust растет часто случается, что вы должны возвращать Result, который может содержать различные типы ошибок. В этом случае вы обычно определяете свой собственный тип ошибки (enum), который может представлять различные виды ошибок. Затем для каждой ошибки, которая может содержаться, вы должны определить черту From в своем собственном перечислении. Это может быть многократной работой, поэтому в quick-error crate есть макрос, который помогает с этим и автоматически реализует черту From для каждой ошибки, которая может содержаться.

Чтобы получить код для компиляции, вы можете определить следующее перечисление ошибки, которое может представлять std::io::Error, а также std::option::NoneError:

quick_error! {
    #[derive(Debug)]
    pub enum FrameError {
        IoError(err: std::io::Error) {from() cause(err)}
        MissingValue(err: std::option::NoneError) {from()}
    }
}

Вместо std::io::Result<Self> вашей функции from_bytes тогда должен вернуть std::result::Result, который использует ваш новый тип ошибки: Result<Self, FrameError>.

Полностью собранный, который выглядит следующим образом:

#![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,
        })
    }
}

Чтобы использовать quick-error ящик, у вас есть добавить следующее к вашему Cargo.toml:

[dependencies]
quick-error = "1.2.3"
...