Как проверить мета-функцию, не требуя, чтобы функция параметра была изменчивой? - PullRequest
1 голос
/ 27 марта 2020

Я написал функцию save, которая принимает функцию в качестве параметра:

fn save(method:&dyn Fn(&'static str)) {
    method("Hello world");
}

fn print(string:&'static str) {
    println!("{}", string);
}

fn main() {
    save(&print)
}

Это прекрасно работает! Но теперь я хочу проверить save. Лучший способ сделать это - использовать FnMut:

fn save(method: &mut dyn FnMut(&'static str)) {
    method("Hello world");
}

fn print(string: &'static str) {
    println!("{}", string);
}

fn main() {
    save(&mut print)
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn save_test() {
        let actual = {
            let mut actual = String::new();
            let mut method = |string: &'static str| {
                actual = format!("{}{}", actual, string);
            };
            save(&mut method);
            save(&mut method);
            actual
        };
        let expected = "Hello worldHello world".to_string();

        assert_eq!(actual, expected);
    }
}

Это все еще работает и выполняет все, что я хочу! Но теперь я должен использовать изменяемые ссылки всякий раз, когда я call сохраняю. Хотя это не влияет на функциональность, оно запутывает код. Есть ли лучший способ достичь sh того же результата?

Ответы [ 2 ]

4 голосов
/ 27 марта 2020

Вы можете использовать RefCell, чтобы получить внутреннюю изменчивость, что позволит вам изменять переменную через общую ссылку. Он идеально подходит для тестирования таких вещей, как:

#[cfg(test)]
mod tests {
    use std::cell::RefCell;
    use super::*;

    #[test]
    fn save_test() {
        let actual = {
            // wrap "actual" in a RefCell, which allows for interior mutability
            let actual = RefCell::new(String::new());
            let method = |string: &'static str| {
                // mutably borrow the string at runtime
                // (can panic in already borrowed, but not a problem here)
                let mut actual = actual.borrow_mut();

                // append string (equivalent to your format!() but can be more
                // efficient)
                actual.push_str(string);
            };

            save(&method);
            save(&method);

            // move string out of RefCell
            actual.into_inner()
        };
        let expected = "Hello worldHello world".to_string();

        assert_eq!(actual, expected);
    }
}

Бег на детской площадке

2 голосов
/ 27 марта 2020

Просто заявите внутри:

fn save(method: impl Fn(&'static str)) {
    method("Hello world");
}

#[test]
fn save_test() {
    let check = |result| {
        assert_eq!(result, "Hello world");
    };
    save(check);
    save(check);
}

(не гарантирует, что функция является вызовом, это обмен)

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...