Сортировать вектор с одним из множества замыканий в зависимости от настройки - PullRequest
0 голосов
/ 23 мая 2018

Я хочу получить этот код для компиляции:

struct Person {
    name: String,
    age: u32,
}

fn main() {
    let sort_by = "age";

    let x = vec![
        Person {
            name: "Peter".to_string(),
            age: 18,
        },
        Person {
            name: "Frank".to_string(),
            age: 55,
        },
    ];

    let key_func;

    if sort_by == "age" {
        key_func = |item: &Person| {
            return &item.age;
        };
    } else if sort_by == "name" {
        key_func = |item: &Person| {
            return &item.name;
        };
    }

    x.sort_by_key(key_func);
}

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

Я знаю, почему это не компилируется, но я не знаю, как яможет решить это.Я попробовал несколько разных вещей, включая завершение замыканий в Box::new(), но не смог заставить его работать.

Сначала я хотел игнорировать проблему и просто переместить всю логику сортировки, включая условные выражения, в одно замыкание, но затем мне пришлось добавить опцию обратной сортировки.Поскольку я действительно хотел бы использовать *_by_key для простоты, я попытался с std::cmp::Reverse, и это произошло:

use std::cmp::Reverse;

fn main() {
    let reverse = false;

    // ....

    let key_func = |item: &Person| {
        if reverse {
            return Reverse(&item.age);
        } else {
            return &item.age;
        }
    };

    x.sort_by_key(key_func);
}

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

Опять же, я понимаюпочему возникает ошибка, но я не уверен, как ее исправить.

Ответы [ 2 ]

0 голосов
/ 23 мая 2018

Вы уже знаете проблему, поэтому мне не нужно объяснять это снова.Вот код.

 let mut cmp_func: Box<Fn(&Person, &Person) -> std::cmp::Ordering> = match sort_by {
    "age" => Box::new(|a, b| a.age.cmp(&b.age)),
    "name" => Box::new(|a, b| a.name.cmp(&b.name)),
    _ => panic!("unknown sort"),
};

if reverse {
    cmp_func = Box::new(move |a, b| cmp_func(a, b).reverse());
}

x.sort_by(|a, b| cmp_func(a, b));

Или более эффективная версия без выделения кучи

x.sort_by(|a, b| {
    let ord = match sort_by {
        "age" => a.age.cmp(&b.age),
        "name" => a.name.cmp(&b.name),
        _ => panic!("unknown sort"),
    };

    if reverse {
        ord.reverse()
    } else {
        ord
    }
});
0 голосов
/ 23 мая 2018

Как вы, кажется, понимаете, проблема в том, что key_func может иметь только один конкретный тип, но каждое из этих двух замыканий имеет разные типы.

Один - это тип, который реализует FnMut(&Person) -> &u32 идругой - это тип, который реализует FnMut(&Person) -> &String.

Даже если они оба возвращают один и тот же тип, , даже если они полностью синтаксически идентичны, они будут двумя разными типами.Это все равно что пытаться сделать let num: i32 = if predicate { 0 } else { "thing" }.Вы можете поместить их в объекты черты с той же чертой, но бокс FnMut s сложно, потому что Box<FnMut...> не реализует FnMut.

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

Одна вещь, которую вы можете сделать, это использовать более общую sort_by, которая равна FnMut(&T, &T) -> Ordering и имеет логику вчтобы узнать, что Ordering вернуть, основываясь на том, какие поля, возможно, руководствуясь каким-либо перечислением SortBy, если вы действительно хотите.

Другой вариант - просто вызвать sort_by_key внутри самих ветвей,так что Vec сортируется исходя из него, или если вы хотите отложить сортировку, затем снова сохраните какой-то индикатор, например, перечисление SortBy, которое вы затем используете для конкретного выполнения сортировки, когда вам действительно нужносделай это.

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