Created
December 15, 2017 10:52
-
-
Save mateon1/80faf7425e09db0891fb9b253f9b7f3a to your computer and use it in GitHub Desktop.
Rust generics
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
struct ParseError<'a> { | |
Whatever(&'a [u8]), | |
Message(String), | |
} | |
/// Parse is a type used as the return type of parser functions | |
/// In case of failure, it holds an error message | |
/// And in case of success, it holds an object of type T, and the bytes that still need to be parsed | |
enum Parse<'a, T> { | |
Success(T, &'a [u8]), | |
Fail(ParseError<'a>) | |
} | |
impl<'a, T> Parse<'a, T> { | |
fn and_then<O, F>(self, f: F) -> Parse<'a, O> | |
where F: FnOnce(T, &'a [u8]) -> Parse<'a, O> { | |
match self { // switch statement on steroids | |
Parse::Success(thing, unparsed) => f(thing, unparsed), | |
Parse::Fail(error) => Parse::Fail(error), | |
} | |
} | |
fn map<O, F>(self, f: F) -> Parse<'a, O> | |
where F: FnOnce(T) -> O { | |
match self { | |
Parse::Success(thing, unparsed) => Parse::Success(f(thing), unparsed), | |
Parse::Fail(error) => Parse::Fail(error), | |
} | |
} | |
} | |
// Example function, take a single byte | |
fn take_byte<'a>(i: &'a [u8]) -> Parse<'a, u8> { // returns Parse object representing that single character | |
match i.split_first() { // Try to split off the first byte off our input, this fails if the input is empty | |
Some((b, rest)) => Parse::Success(b, rest), | |
None => Parse::Fail(ParseError::Message("Expected character, got EOF".into())) | |
} | |
} | |
// Terrible example | |
struct Thing { | |
c1: u8, | |
c2: u8, | |
c3: u8, | |
} | |
fn parse_whatever<'a>(i: &'a [u8]) -> Parse<'a, Thing> { | |
take_byte(i).and_then(|char1, rest| { | |
take_byte(rest).map(move |char2, rest| { | |
(char1, char2) | |
}) | |
}).and_then(|(char1, char2), rest| { | |
take_byte(rest).map(move |char3, rest| { | |
Thing { | |
c1: char1, | |
c2: char2, | |
c3: char3, | |
} | |
}) | |
}) | |
} | |
// These .and_then()s CAN'T be annotated, because the closure type can't be expressed in Rust | |
// But if it could, it would look something like this | |
fn parse_whatever<'a>(i: &'a [u8]) -> Parse<'a, Thing> { | |
take_byte(i).and_then<(u8, u8), {closure_type}>(|char1: u8, rest: &'a [u8]| -> Parse<'a, (u8, u8)> { | |
take_byte(rest) | |
.map<(u8, u8), {closure_type}>(move |char2: u8, rest: &'a [u8]| -> (u8, u8) { | |
(char1, char2) | |
}) | |
}).and_then<Thing, {closure_type}>(|(char1: u8, char2: u8), rest: &'a [u8]| -> Parse<'a, Thing> { | |
take_byte(rest).map<Thing, {closure_type}>(move |char3: u8, rest: &'a [u8]| -> Thing { | |
Thing { | |
c1: char1, | |
c2: char2, | |
c3: char3, | |
} | |
}) | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment