Закрытие ржавчины и несоответствие fn

#function #rust #closures

#функция #Ржавчина #закрытие

Вопрос:

Я пытаюсь сделать что-то, что является нормальным на функциональном языке, т.Е. {F #, SML, OCaml, Haskel ..}, передавая функции в качестве ввода и вывода. Но я думаю, что я еще не закончил, когда дело доходит до системы типов Rust. Приведенный ниже код — это часть моего кода, которая выдает ошибку. Короче говоря, я создаю структуру, которая содержит функцию, которая что-то делает и возвращает какую-то другую вещь с возможной ошибкой. У функции or есть проблема, поскольку мне нужно включить, что мне когда-нибудь в будущем понадобится передать входные данные в функцию запуска, которую я использую closure. Я пытался использовать Fn в качестве типа, но компилятор все равно выдает ошибку.

 pub struct Pattern<T>{
   pat : fn(amp;str) -> Result<(T, amp;str),amp;'static str>
}

impl<T> Pattern<T> {
    pub fn run(self, input: amp;str) -> Result<(T,amp; str), amp;'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),
                    } 
                }
            }
        }  
    }
}
  

Комментарии:

1. Похоже, что у вашего сокращенного кода есть еще одна проблема, в методе of вы используете переменные pat1 и pat2 , которые не определены.

2. небольшая опечатка, вызванная попыткой заставить ее работать. но это все еще не работает

Ответ №1:

Я не очень уверен в приведенном ниже решении, но кто знает…

Во-первых, or() вы пытаетесь предоставить результирующее Pattern закрытие захвата, где ожидается функция (без захвата); как вы указали в своем вопросе, Fn признак кажется более подходящим для pat члена. Чтобы фактически сохранить его, Box<dyn Fn( ...> можно использовать.

Более того, run() и or() методы потребляют Pattern s, поэтому каждый Pattern можно использовать только один раз, что, вероятно, не то, что вы хотите. Я предлагаю вместо этого использовать ссылки.

Результирующий Pattern in or возвращается по значению (что, на мой взгляд, правильно); тогда все локальные переменные, которые необходимо захватить его замыканию ( self и pat параметры), должны быть move объединены в замыкание.

Поскольку теперь у нас есть много Pattern ссылок друг на друга, нам приходится явно иметь дело со временем жизни. Например, два Pattern s, объединенные в or() , должны пережить результирующий Pattern . Эти ограничения распространяются по всему коду.

Наконец, была предпринята попытка проиллюстрировать использование в main() .

(Возможно, более опытные программисты Rust найдут более простой способ добиться этого или некоторые ошибки)

 pub struct Pattern<'p, T> {
    pat: Box<dyn Fn(amp;str) -> Result<(T, amp;str), amp;'static str>   'p>,
}

impl<'p, T> Pattern<'p, T> {
    pub fn run<'s>(
        amp;self,
        input: amp;'s str,
    ) -> Result<(T, amp;'s str), amp;'static str> {
        (self.pat)(input)
    }

    pub fn or<'a, 'b>(
        amp;'p self,
        pat: amp;'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, amp;input[0..4]))
            } else {
                Err("too short for p1")
            }
        }),
    };
    let p2 = Pattern {
        pat: Box::new(|input| {
            if input.len() >= 2 {
                Ok((2, amp;input[2..]))
            } else {
                Err("too short for p2")
            }
        }),
    };
    let p3 = p1.or(amp;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"))
    */
}
  

Комментарии:

1. Он жалуется на то, что не может переместить self / pat в закрытие, потому что оно захвачено признаком Fn?

2. @kam Я только что попробовал еще раз с предоставленным кодом, и он не жалуется. Вы что-то изменили?

3. да, я не изменял функцию запуска, но это очень помогло: D