Может ли функция, которая принимает ссылку, быть передана в качестве аргумента закрытия, который предоставит собственные значения? - PullRequest
1 голос
/ 12 апреля 2019

Я пытаюсь упростить свои замыкания, но у меня была проблема с преобразованием моего замыкания в ссылку на связанную функцию, когда параметр принадлежит закрытию, но внутренний вызов функции ожидает только ссылку.

#![deny(clippy::pedantic)]

fn main() {
    let borrowed_structs = vec![BorrowedStruct, BorrowedStruct];

    //Selected into_iter specifically to reproduce the minimal scenario that closure gets value instead of reference
    borrowed_structs
        .into_iter()
        .for_each(|consumed_struct: BorrowedStruct| MyStruct::my_method(&consumed_struct));
    // I want to write it with static method reference like following line:
    // for_each(MyStruct::my_method);
}

struct MyStruct;
struct BorrowedStruct;

impl MyStruct {
    fn my_method(prm: &BorrowedStruct) {
        prm.say_hello();
    }
}

impl BorrowedStruct {
    fn say_hello(&self) {
        println!("hello");
    }
}

Детская площадка

Можно ли упростить этот код:

into_iter().for_each(|consumed_struct: BorrowedStruct| MyStruct::my_method(&consumed_struct));

К следующему:

into_iter().for_each(MyStruct::my_method)

Обратите внимание, что into_iter здесь только для того, чтобы воспроизвести сценарий, которому принадлежит мое значение в моем замыкании. Я знаю, что iter может использоваться в таком сценарии, но это не реальный сценарий, над которым я работаю.

Ответы [ 2 ]

1 голос
/ 12 апреля 2019

Ответ на ваш общий вопрос: нет .Типы должны точно совпадать при передаче функции в качестве аргумента закрытия.

Существуют одноразовые обходные пути, как показано в ответе Родриго , но общее решение состоит в том, чтобы просто взять ссылку самостоятельно,как вы сделали:

something_taking_a_closure(|owned_value| some_function_or_method(&owned_value))

Я на самом деле выступал за это дело около двух лет назад в рамках модернизации эргономики, но никто больше не интересовался.


В вашем конкретном случае вы можете удалить тип из аргумента замыкания, чтобы сделать его более кратким:

.for_each(|consumed_struct| MyStruct::my_method(&consumed_struct))
1 голос
/ 12 апреля 2019

Я не думаю, что в черте Iterator есть for_each_ref. Но вы можете написать свой собственный довольно легко (детская площадка) :

trait MyIterator {
    fn for_each_ref<F>(self, mut f: F)
    where
        Self: Iterator + Sized,
        F: FnMut(&Self::Item),
    {
        self.for_each(|x| f(&x));
    }
}
impl<I: Iterator> MyIterator for I {}
borrowed_structs
    .into_iter()
    .for_each_ref(MyStruct::my_method);

Другой вариант, если вы можете изменить прототип функции my_method, вы можете заставить его принимать значение либо по значению, либо по ссылке с заемом:

impl MyStruct {
    fn my_method(prm: impl Borrow<BorrowedStruct>) {
        let prm = prm.borrow();
        prm.say_hello();
    }
}

И тогда ваш оригинальный код с .for_each(MyStruct::my_method) просто работает.

Третий вариант - использовать универсальную функцию-обертку (детская площадка) :

fn bind_by_ref<T>(mut f: impl FnMut(&T)) -> impl FnMut(T) {
    move |x| f(&x)
}

И затем вызвать упакованную функцию с помощью .for_each(bind_by_ref(MyStruct::my_method));.

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