首页 > 解决方案 > 根据设置对具有多个闭包之一的向量进行排序

问题描述

我想让这段代码编译:

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);
}

操场

同样,我明白为什么会发生错误,但我不知道如何解决它。

标签: sortingrustclosures

解决方案


正如您似乎认识到的那样,问题在于key_func只能有一个特定类型,但是这两个闭包中的每一个都有不同的类型。

一个是实现的类型,FnMut(&Person) -> &u32另一个是实现的类型FnMut(&Person) -> &String

即使它们都返回相同的类型,即使它们在语法上完全相同,它们也将是两种不同的类型。这就像尝试做的一样let num: i32 = if predicate { 0 } else { "thing" }。您可以将它们装箱到具有相同特征的特征对象中,但是装箱FnMuts 很棘手,因为Box<FnMut...>它没有实现FnMut.

还有一个更根本的问题:不能使用sort_by_key返回引用的函数

您可以做的一件事是使用更通用的sort_by,其中FnMut(&T, &T) -> Ordering有逻辑可以根据哪些字段知道要返回什么,如果您真的愿意Ordering,也许可以通过某种枚举来指导。SortBy

另一种选择是简单地sort_by_key在分支内部调用,以便从中Vec排序出来,或者如果您想推迟排序,则再次维护某种指标,例如SortBy枚举,然后使用它来具体执行排序当你真正需要做的时候。


推荐阅读