kam
kam

Reputation: 669

Rust closure and fn mismatch

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

Answers (1)

prog-fh
prog-fh

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 Patterns, 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 moved into the closure.

Since we have now many Patterns referring to each others, we have to deal explicitly with lifetimes. For example, the two Patterns 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

Related Questions