2017-12-30 26 views
1

私はコンテキストとして行番号を持っているので、私はラインで、テキストファイルの行を解析しています:Errへのコンテキストの追加を自動化するにはどうすればよいですか?

#[derive(Debug, Clone)] 
pub struct Position { 
    pub line: usize, 
    pub column: usize, 
} 

#[derive(Debug)] 
pub enum ParseError { 
    IoError(io::Error), 
    InvalidRecord(Position), 
    EncodingError(Position), 
} 

私はこのようなループがあります。

let mut pos = Position { line: 0, column: 0 }; 
const LF: u8 = 0xa; 
let mut record_buf = Vec::new(); 
while let Ok(nbytes) = reader.read_until(LF, &mut record_buf) { 
    // if record_buf contains some special bytes, then 
    // we have several numbers in ASCII 
    let x = str::from_utf8(&record_buf[42..(42 + 2)])?.parse::<u32>()?; 
    let y = str::from_utf8(&record_buf[46..(46 + 4)])?.parse::<u32>()?; 

    //at the end 
    record_buf.clear(); 
    pos.line += 1; 
} 

を私はParseError::EncodingErrorにマッピングUtf8Errorを自動化したいですおよび ParseIntError~ParseError::EncodingError

impl From<Utf8Error> for ParseError、 を実装することはできません。行番号の形式のコンテキストが特性実装で使用できないためです。

コードを簡略化し、Vec<u8>から抽出したいすべての番号について、このように冗長なエラー処理を記述しないでください。

str::from_utf8(&record_buf[42..(42 + 2)]) 
    .map_err(|_| ParseError::EncodingError(pos.clone()))? 
    .parse::<u32>() 
    .map_err(|_| ParseError::InvalidRecord(pos.clone()))? 
+1

は、あなたが読書やエラーの変換を行う関数を作成しようとしたことがありますか? – red75prime

+0

@ red75prime私はいくつかのより一般的な方法で問題を解決したい。私は構文解析の作業を開始し、 "ヘッダ"行を解析した後、他の多くのエラーを処理しなければならず、そのすべてに対して行と列のコンテキストを追加します。 – user1244932

答えて

2

TL; DRは:quick_errorerror-chain、又はfailureようクレートを使用。本当だが、それはあなたがコンテキストを運ぶタイプを生成することができないという意味ではありません


I can not just implement impl From<Utf8Error> for ParseError , because the context in the form of line number is not available in the trait implementation.

。あなたはこのようなものまでお電話サイトを簡素化することができます

:そうするために

let val = str::from_utf8(&record_buf[4..][..2]) 
    .context(pos)? 
    .parse() 
    .context(pos)?; 

、私たちは私たちの組み合わせの文脈と元のエラーを保持する新しいタイプを作成し、その後、Resultため延長トレイトを実装エラーにコンテキストを追加する:

struct Context<V, E>(V, E); 

trait ContextExt<T, E> { 
    fn context<V>(self, v: V) -> Result<T, Context<V, E>>; 
} 

impl<T, E> ContextExt<T, E> for Result<T, E> { 
    fn context<V>(self, v: V) -> Result<T, Context<V, E>> { 
     self.map_err(|e| Context(v, e)) 
    } 
} 

私たちは、その後、各面白いことのためにFrom<Context<...>> for Errorを実装します.clone()へのこれ以上のコール -

impl From<Context<Position, str::Utf8Error>> for ParseError { 
    fn from(other: Context<Position, str::Utf8Error>) -> ParseError { 
     ParseError::EncodingError(other.0, other.1) 
    } 
} 

impl From<Context<Position, num::ParseIntError>> for ParseError { 
    fn from(other: Context<Position, num::ParseIntError>) -> ParseError { 
     ParseError::InvalidRecord(other.0, other.1) 
    } 
} 

最後の人間工学に基づいた変更は、それははるかに簡単に使用できるようになり、あなたのPostionタイプ、のためCopyを実装することです。

Playground


前述の木箱がこの方法で容易になります。ここで

はクイックエラーを持つすべてのコードです(私のお気に入り):

#[macro_use] 
extern crate quick_error; 

use quick_error::ResultExt; 
use std::{num, str}; 

#[derive(Debug, Copy, Clone)] 
pub struct Position { 
    pub line: usize, 
    pub column: usize, 
} 

quick_error! { 
    #[derive(Debug)] 
    pub enum ParseError { 
     EncodingError(pos: Position, err: str::Utf8Error) { 
      context(pos: Position, err: str::Utf8Error) -> (pos, err) 
     } 
     InvalidRecord(pos: Position, err: num::ParseIntError) { 
      context(pos: Position, err: num::ParseIntError) -> (pos, err) 
     } 
    } 
} 

fn inner_main() -> Result<u32, ParseError> { 
    let record_buf = b"kode12abc"; 

    let pos = Position { line: 1, column: 2 }; 

    let val = str::from_utf8(&record_buf[4..][..2]) 
     .context(pos)? 
     .parse() 
     .context(pos)?; 

    Ok(val) 
} 

fn main() { 
    let v = inner_main().expect("boom"); 
    println!("{}", v) 
} 
関連する問題