有时候需要慎重选择 true 还是 false 。
#[derive(Debug)]
struct User { id: u32, pid: u32, name: String, }
fn set_up_option() -> Vec<Option<User>> {
// 注意修改了顺序
vec![Some(User { id: 0, pid: 1, name: "admin1".to_string(), }),
Some(User { id: 2, pid: 2, name: "admin3".to_string(), }),
Some(User { id: 1, pid: 1, name: "admin2".to_string(), }),
Some(User { id: 3, pid: 2, name: "admin4".to_string(), })]
}
/// binary_search 系的方法有非常重要的前提:sorted
/// 这里说的排序专指升序方法,它并不支持降序数据,例子:
/// https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=4696a39e4e60c9498a2be05a1c7e9c64
///
/// 然而这的数据有几个地方可能不满足 binary_search 的前提:
/// 1. 结构体可能需要支持排序 :
/// #[derive(Debug, PartialEq, PartialOrd, Eq, Ord, Clone)]
/// 2. 上一条不是必须的,但这一条必须满足,你得让你的数据是升序的,因为 binary_search
/// 不会给你排好序。要么你十分确信数据按照某种方式升序(比如这里按照 pid 升序),
/// 要么使用 `sort` 或者 `sort_by_key` 让数据升序,这里更建议使用后者,因为前者需要修改一些代码
/// (比如添加第一条所列的 traits、并且让结构体的 pid 字段放在首位)
/// 3. 此外,binary_search_by_key 比较的序列依然必须是升序的,而使用 bool 时,
/// true / false 的判断逻辑会影响原升序数据的逻辑结果是降序或者升序,见下面的例子
/// 4. 所以,必须始终确保 binary_search 的数据升序,否则针对小型数据,选择迭代器省事
fn main() {
// binary_search_by_key: 检索 pid 为 1 的 user
let mut users_option = set_up_option();
users_option.sort_by_key(|x| x.as_ref().unwrap().pid); // 还需处理这里可能出现的 None ,而使用迭代器,None 很容易处理
// println!("sorted: {:#?}", users_option);
// wrong
let res = users_option.binary_search_by_key(&true, |x| matches!(x, Some(user) if user.pid == 1));
println!("\npid == 1 [wrong]: {:?}", res); // Err(4)
// correct
let res = users_option.binary_search_by_key(&false, |x| matches!(x, Some(user) if user.pid != 1));
println!("pid == 1 [correct]: {:?}", res); // Ok(1)
println!("\n这里 true / false 的逻辑等价于 ↓");
// wrong
let m: Vec<u8> = users_option.iter().map(|x| matches!(x, Some(user) if user.pid == 1) as u8).collect();
let res = m.binary_search(&1);
println!("not ascending: {:?}", m); // [1, 1, 0, 0]
println!("thus [wrong]: {:?}", res); // Err(4)
// correct
let m: Vec<u8> = users_option.iter().map(|x| !matches!(x, Some(user) if user.pid == 1) as u8).collect();
let res = m.binary_search(&0);
println!("ascending: {:?}", m); // [0, 0, 1, 1]
println!("[correct]: {:?}", res); // Ok(1)
// 使用迭代器就无需排序,而且无需给结构体添加不必要的 traits,处理含 Option 或 Result 的数据也十分方便
// 尤其是,有时候你根本无法给结构体添加 `Eq` `Ord` (比如结构体字段类型有浮点型)
let users_option = set_up_option();
let f = users_option.iter().enumerate() // 你需要索引的话就加这个
.filter(|&(_, x)| matches!(x, Some(user) if user.pid == 1));
let res = f.clone().next().unwrap(); // 你只需要第一个出现的就使用 `next`
println!("\nuse iter [correct]: {:?}", res); // (0, Some(User { id: 0, pid: 1, name: "admin1" }))
let res: Vec<_> = f.collect(); // 需要所有出现的结果就 `collect`
println!("use iter [correct]: {:#?}", res); // [(0, ...), (2, ...)]
}