“我的第一次生锈”:枚举 `Option<&mut ...>` 存在方法 `unwrap_or_default`,但不满足其特征界限

Posted

技术标签:

【中文标题】“我的第一次生锈”:枚举 `Option<&mut ...>` 存在方法 `unwrap_or_default`,但不满足其特征界限【英文标题】:"my first rust": the method `unwrap_or_default` exists for enum `Option<&mut ...>`, but its trait bounds were not satisfied 【发布时间】:2021-07-13 20:50:27 【问题描述】:

我的第一个rust 程序/函数应该遍历目录树并提供HashMapK(mime_as_string)V(count_sum, size_sum)。我想要它的 FP 风格。

所以我拥有的是这样的:

fn files_info4(rootpath: &str) 

    struct FTypeStats 
        count: u64,
        size: u64,
    

    impl Default for FTypeStats 
        fn default() -> Self 
            FTypeStats 
                count: 0,
                size: 0,
            
        
    

    // get file type stats directly/functionally/lazy
    let fts = WalkDir::new(rootpath)
    .min_depth(1)
    .max_depth(99)
    .into_iter()
    .filter_map(|entry| entry.ok())
    .map(|entry| (entry, entry.metadata().map_or(false, |m| m.is_file())))
    .filter(|(e, f)| *f)
    .fold(HashMap::new(), | mut acc: HashMap<String, FTypeStats>, (e, _) | 
        let ftype = tree_magic::from_filepath(e.path());
        acc.get_mut(&ftype).unwrap_or_default().count += 1;
        acc.get_mut(&ftype).unwrap_or_default().size += e.metadata().map_or(0, |m| m.len());
        acc
    );

遇到错误后我在哪里做impl Default

error[E0599]: the method `unwrap_or_default` exists for enum `Option<&mut FTypeStats>`, but its trait bounds were not satisfied
  --> src/main.rs:54:29
   |
54 |         acc.get_mut(&ftype).unwrap_or_default().count += 1;
   |                             ^^^^^^^^^^^^^^^^^ method cannot be called on `Option<&mut FTypeStats>` due to unsatisfied trait bounds
   |
   = note: the following trait bounds were not satisfied:
           `&mut FTypeStats: Default`

但它仍然没有帮助,即仍然存在相同的错误。

问题:

    如何编译/运行 可以优化评估链吗? (fold 中的代码实际上是否正确,例如我需要为 FTypeStats 进行的分配和初始化默认/初始值?(感觉有点“隐含”))

TIA!

【问题讨论】:

【参考方案1】:

这里有多个问题:首先,在HashMap 上调用.get_mut() 会给您一个Option,即None,以防密钥不在地图中。 Option 本身与地图分离(除了生命周期约束),因此在None-case 中,没有链接返回到地图中,可以将默认构造的值放在适当的位置。

其次,正如错误消息所说,方法unwrap_or_default()需要Option的内部类型才能实际实现Default,在本例中为&amp;mut FTypeStats。这样的实现不仅在您的代码中不存在,而且这样做也非常困难,因为凭空生成对某种类型的(有效)可变引用 - 这是Default 的重点 - 实际上是不可能的。

解决方案是在HashMap 上使用entry() API 而不是get_mut()

*acc.entry(&ftype).or_default().count += 1;

entry() 的调用为您提供了一个代理值,指向&amp;ftype 位于或将位于HashMap 中的任何位置。对or_default() 的调用总是会给你一个&amp;mut FTypeStats。如果&amp;ftype 不在映射中,它使用Default on FTypeStats(不是Default on &amp;mut FTypeStats)生成一个新条目,插入它,并为您提供一个可变引用。

【讨论】:

谢谢,这让我更接近了,虽然我现在遇到了一个新问题:如果我的哈希键是一个值 (String) 我得到 使用移动值:ftype 我第二次索引(对于+size);如果我尝试通过使用引用(例如&amp;str)作为键来解决这个问题,我会得到(对于两个索引)ftype 的寿命不够长。如果我尝试通过为FTypeStats 实现add 并像这样添加acc.entry(ftype).or_default().add(FTypeStatscount: 1, size: e.metadata().map_or(0, |m| m.len())); 来解决那个(天真?),它会运行但所有值都是0。 (而且比我的go 版本慢很多)

以上是关于“我的第一次生锈”:枚举 `Option<&mut ...>` 存在方法 `unwrap_or_default`,但不满足其特征界限的主要内容,如果未能解决你的问题,请参考以下文章

Rust学习教程21 - Option和模式匹配

Rust学习教程21 - Option和模式匹配

如何在 C# 中获取枚举项? [复制]

使用 select2 列表连接枚举

Rust语言圣经21 - Option和模式匹配

Rails 5.2 API - 在JSON中返回枚举值