Skip to content

Instantly share code, notes, and snippets.

@mateon1
Created December 15, 2017 10:52
Show Gist options
  • Save mateon1/80faf7425e09db0891fb9b253f9b7f3a to your computer and use it in GitHub Desktop.
Save mateon1/80faf7425e09db0891fb9b253f9b7f3a to your computer and use it in GitHub Desktop.
Rust generics
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