2017-01-21 10 views
4

다른 구문 분석 함수를 전달하고 다른 Iteratee을 얻을 수 있도록 Iteratee 구조를 일반화하려고합니다.HKT (Higher kinded type) 지원없이 Rust에서 일반 구조를 만들려면 어떻게해야합니까?

use std::io::{BufRead, BufReader}; 
use std::str::{from_utf8, Utf8Error}; 

#[derive(PartialEq, Debug)] 
struct Cat<'a> { 
    name: &'a str, 
} 

fn parse<'a>(slice: &'a [u8]) -> Result<Cat<'a>, Utf8Error> { 
    from_utf8(slice).map(|name| Cat { name: name }) 
} 

struct Iteratee<R> 
    where R: BufRead + Sized 
{ 
    read: R, 
} 

impl<R> Iteratee<R> 
    where R: BufRead + Sized 
{ 
    fn next<'a, F>(&'a mut self, fun: F) 
     where F: Fn(Option<Result<Cat<'a>, Utf8Error>>) ->() + Sized 
    { 
     let slice = self.read.fill_buf().unwrap(); 
     fun(Some(parse(slice))) 
     //  ^^^^^^^^^^^ How do I pull 'parse' up as a function of Iteratee 
    } 
} 

fn main() { 
    let data = &b"felix"[..]; 
    let read = BufReader::new(data); 
    let mut iterator = Iteratee { read: read }; 
    iterator.next(|cat| assert_eq!(cat.unwrap().unwrap(), Cat { name: "felix" })); 
} 

이 그것이 일반적인 제작에서 내 시도,하지만 난이 함수에 대한 참조 또는 폐쇄를 전달하거나 함께 IterateeFun을 만들 수 없습니다 :이 작품이 아닌 일반 버전입니다.

struct IterateeFun<R, P, T> 
    where R: BufRead + Sized, 
      P: Fn(&[u8]) -> (Result<T, Utf8Error>) + Sized 
{ 
    read: R, 
    parser: P, 
} 

impl<R, P, T> IterateeFun<R, P, T> 
    where R: BufRead + Sized, 
      P: Fn(&[u8]) -> (Result<T, Utf8Error>) + Sized 
{ 
    fn next<'a, F>(&'a mut self, fun: F) 
     where F: Fn(Option<Result<T, Utf8Error>>) ->() + Sized 
    { 
     let slice = self.read.fill_buf().unwrap(); 
     fun(Some((self.parser)(slice))) 
    } 
} 


fn main() { 
    let data = &b"felix"[..]; 
    let read = BufReader::new(data); 
    let mut iterator = IterateeFun { 
     read: read, 
     parser: parse, // What can I put here? 
     // I've tried a closure but then I get error[E0495]/ lifetime issues 
    }; 

    iterator.next(|cat| assert_eq!(cat.unwrap().unwrap(), Cat { name: "felix" })); 
} 

그림과 같이 함수에 함수를 전달하는 방법을 알고 싶습니다. 아니면 내가 대신이 특성으로해야합니까?

+1

입니다. 오류 [E0495]는 실제 문제가 아니며, 함수를 전달하거나 최소한 코드의 제네릭 버전을 만들 수있는 방식으로 코드를 재구성하는 방법입니다. 어쩌면 나는 관용적 인 녹 방법으로 이것을 생각하지 않을 것입니다. 이것은 내가 특성에 대한 언급으로 언급하려고했던 것입니다. 요약하면 내가 모르는 것을 알지 못합니다.) –

+0

[녹스에 클로저를 저장하는 방법은 무엇입니까?] (http://stackoverflow.com/q/27831944/155423), [저장 중 HashMap의 클로저] (http://stackoverflow.com/q/29202137/155423) 또는 [구조에 클로저 저장 - 적절한 평생을 추론 할 수 없음] (http://stackoverflow.com/q/21510694/) 155423) (특별히 검토하지는 않았지만 초기 검색 결과 일뿐입니다). – Shepmaster

+0

[higher-rank trait bounds] (https://doc.rust-lang.org/beta/nomicon/hrtb.html)를 사용하는 답변을 게시했습니다. [예 : 놀이터에서보기] (https : // gist .github.com/07a68fd62b03ae54659716e118d2b1da). 그러나 여전히 두 번째 스 니펫만큼 일반적이지는 않습니다. 클로저가 'T <'a>'이 아니라 'Result '을 반환한다고 가정합니다. 후자가 [더 높은 종류의 유형]이없는 현재 녹에서 표현하는 것이 불가능할 수도 있습니다 (https://users.rust-lang.org/t/does-rust-really-need-higher). -kinded-types/5531). 이것이 타입 시스템에 대한 나의 이해를 넘어선 것처럼, 나는 나의 대답을 제거했다. – user4815162342

답변

2

대부분의 문제와 마찬가지로 나는 다른 수준의 간접 참조가 필요했습니다. 더 높은 kinded types (HKT)가 분명히 도움이 될 것이지만 실제로는 파싱 함수와 평생 매개 변수를 연결할 수 있어야합니다. user4815162342 및 Streamer에서 영감을

는 내가 관련 유형이 개 특성 Iteratee<'a>Parser<'a> 각을 만들 수 있습니다 실현하고 내가 그들을 결합 구현을 만들 때 나는 나에게 HKTs의 양식을 제공하기 위해 관련 유형을 결합 할 수있을 것입니다 :

trait Parser<'a> { 
    type Output: 'a; 

    fn parse(&self, &'a [u8]) -> Result<Self::Output, Utf8Error>; 
} 

struct CatParser; 

impl<'a> Parser<'a> for CatParser{ 
    type Output = Cat<'a>; 

    fn parse(&self, slice: &'a [u8]) -> Result<Self::Output, Utf8Error> { 
     parse(slice) 
    } 
} 

trait Iteratee<'a> { 
    type Item: 'a; 

    fn next<F>(&'a mut self, fun: F) where F: Fn(Option<Self::Item>) ->() + Sized; 
} 

struct IterateeParser<R, P> { 
    read: R, 
    parser: P, 
} 

impl<'a, R, P> Iteratee<'a> for IterateeParser<R,P> where R: BufRead + Sized, P: Parser<'a> { 
    type Item = Result<P::Output, Utf8Error>; 
    //     ^^^^^^^^^ This is the magic! 

    fn next<F>(&'a mut self, fun: F) where F: Fn(Option<Self::Item>) ->() + Sized { 
     let slice = self.read.fill_buf().unwrap(); 
     fun(Some(self.parser.parse(slice))) 
    } 
} 

fn main() { 
    let data = &b"felix"[..]; 
    let read = BufReader::new(data); 
    let mut iterator = IterateeParser { read: read, parser: CatParser }; 
    iterator.next(|cat| assert_eq!(cat.unwrap().unwrap(), Cat { name: "felix" })); 
} 

마법 라인은 공정 점은 심지어 올바른 용어가 내가 가지고있는 문제를 설명하기 위해 무엇 확실하지 않다 순간에,이다 type Item = Result<P::Output, Utf8Error>;