Reputation: 669
I'm trying to do something that is normal in functional language i.e. {F#, SML, OCaml, Haskel..} passing functions around as input and output. But I think I'm not quit there yet when it comes to Rust type system. The code below is the part of my code that is making error. In short I'm making a struct that hold a function, that do something and return some other thing with possible error. The or function has the problem, since I need to incorporate that I some time in the future need to pass an input into the run function I'm using closure. I h ave tried using Fn as type but it still make the compiler through an error.
pub struct Pattern<T>{
pat : fn(&str) -> Result<(T, &str),&'static str>
}
impl<T> Pattern<T> {
pub fn run(self, input: &str) -> Result<(T,& str), &'static str> {
(self.pat)(input)
}
pub fn or(self, pat: Pattern<T>) -> Pattern<T> {
Pattern {
pat: |input| match self.run(input) {
Ok(ret) => Ok(ret),
Err(msg) => {
match pat.run(input) {
Ok(ret) => Ok(ret),
Err(msg) => Err(msg),
}
}
}
}
}
}
Upvotes: 2
Views: 256
Reputation: 16805
I am not extra-confident about the solution below, but who knows...
First, in or()
you try to provide the resulting Pattern
a capturing
closure where a function (without capture) is expected; as you stated
in your question, a Fn
trait seems more appropriate for the pat
member.
In order to actually store it, Box<dyn Fn( ...>
can be used.
Moreover, the run()
and or()
methods consume the Pattern
s, thus
each Pattern
can only be used once, which is probably not what you want.
I suggest using references instead.
The resulting Pattern
in or
is returned by value (which is correct,
in my opinion); then all the local variables its closure needs to capture
(self
and pat
parameters) have to be move
d into the closure.
Since we have now many Pattern
s referring to each others,
we have to deal explicitly with lifetimes. For example, the two Pattern
s
combined in or()
must outlive the resulting Pattern
.
These constraints propagate all through the code.
Finally, an attempt was made to illustrate the usage in main()
.
(Probably more experienced Rust programmers will find a simpler way to achieve this, or some errors)
pub struct Pattern<'p, T> {
pat: Box<dyn Fn(&str) -> Result<(T, &str), &'static str> + 'p>,
}
impl<'p, T> Pattern<'p, T> {
pub fn run<'s>(
&self,
input: &'s str,
) -> Result<(T, &'s str), &'static str> {
(self.pat)(input)
}
pub fn or<'a, 'b>(
&'p self,
pat: &'a Pattern<T>,
) -> Pattern<'b, T>
where
'p: 'b,
'a: 'b,
{
Pattern {
pat: Box::new(move |input| match self.run(input) {
Ok(ret) => Ok(ret),
Err(_msg) => match pat.run(input) {
Ok(ret) => Ok(ret),
Err(msg) => Err(msg),
},
}),
}
}
}
fn main() {
let p1 = Pattern {
pat: Box::new(|input| {
if input.len() >= 4 {
Ok((1, &input[0..4]))
} else {
Err("too short for p1")
}
}),
};
let p2 = Pattern {
pat: Box::new(|input| {
if input.len() >= 2 {
Ok((2, &input[2..]))
} else {
Err("too short for p2")
}
}),
};
let p3 = p1.or(&p2);
println!("p1: {:?}", p1.run("a"));
println!("p2: {:?}", p2.run("a"));
println!("p3: {:?}", p3.run("a"));
println!("~~~~~~~~~~~~~~~~");
println!("p1: {:?}", p1.run("abc"));
println!("p2: {:?}", p2.run("abc"));
println!("p3: {:?}", p3.run("abc"));
println!("~~~~~~~~~~~~~~~~");
println!("p1: {:?}", p1.run("abcdef"));
println!("p2: {:?}", p2.run("abcdef"));
println!("p3: {:?}", p3.run("abcdef"));
/*
p1: Err("too short for p1")
p2: Err("too short for p2")
p3: Err("too short for p2")
~~~~~~~~~~~~~~~~
p1: Err("too short for p1")
p2: Ok((2, "c"))
p3: Ok((2, "c"))
~~~~~~~~~~~~~~~~
p1: Ok((1, "abcd"))
p2: Ok((2, "cdef"))
p3: Ok((1, "abcd"))
*/
}
Upvotes: 1