DEBUG.WIKI
阿里云;从建站到云上业务;云服务器 · 数据库 · 对象存储;查看产品 →
野草云;香港 200Mbps 峰值;不限月流量 · BGP VPS;查看套餐 →
V.PS:让项目靠近用户,亚太、欧洲、北美 KVM VPS

Rust 1.100 beta 的 Vec::from_fn 撞上扩展 trait,编译报 expected usize

Rust 1.100 beta 新 Vec::from_fn 抢先解析旧 trait 同名调用,出现 E0308。用完全限定路径保留原实现,再判断长度与索引迁移。

项目此前用扩展 trait 提供 Vec::from_fn,切到 Rust 1.100 beta 后,旧调用突然报 E0308:expected usize, found u32。先看诊断中的函数定义位置;如果它已经指向标准库 Vec,就不再是原来的 trait 实现。

1.100 beta 加入了 Vec 的固有关联函数 from_fn(length: usize, f: FnMut(usize) -> T)。同名调用现在会优先解析到这份实现,因此旧扩展函数的参数类型与生成规则都需要重新确认。这里讨论 beta 升级预检,不把它写成 Rust 1.99 正式版新出现的回归,也不保证 beta 接口之后不会变化。

明确调用原 trait,而不是先把 u32 改成 usize

如果仍要保留项目的生成语义,用完全限定语法指定原来的 trait:

let values = <Vec<Item> as ProjectVecFactory<Item>>::from_fn(count, make_item);

这是一段调用形式,Item、ProjectVecFactory、count 和 make_item 要替换为项目现有类型、trait 与参数。真正的 trait 可能没有泛型参数,函数也可能有不同签名,不能把上例名称当作标准库接口直接导入。

下面用一个完整的小型扩展说明这种消歧方式,它不是要求替换原业务实现:

trait IndexedVec<T> {
    fn from_fn<F: FnMut(u32) -> T>(count: u32, f: F) -> Vec<T>;
}

impl<T> IndexedVec<T> for Vec<T> {
    fn from_fn<F: FnMut(u32) -> T>(count: u32, f: F) -> Vec<T> {
        (0..count).map(f).collect()
    }
}

fn main() {
    let values = <Vec<u32> as IndexedVec<u32>>::from_fn(3, |i| i + 10);
    assert_eq!(values, vec![10, 11, 12]);
}

完全限定路径让编译器选择 IndexedVec 对 Vec 的实现,不再与固有函数争用简写名字。它适合保留现有行为;长期维护时,也可以给项目函数换一个更具体的名称,并同步所有调用点。

有意迁移到标准库时,逐项对齐生成规则

若决定使用新的 Vec 固有函数,确认长度类型为 usize,闭包接收的是从零开始的 usize 索引,返回元素类型也与原接口一致。原 trait 可能接收 u32、忽略索引、按其他范围生成,或包含额外初始化逻辑。仅按编译器建议转一次整数,不会自动保留这些行为。

来自外部数据的长度还要处理平台范围与业务上限,不能直接加 as usize 或不加判断地 unwrap 转换结果。长度能转换也不表示允许分配这么多元素;这份内存约束与名字冲突是两个检查。

升级前保存工具链与依赖锁定,先在 beta 检查分支核对实际 rustc --version、完整 E0308 位置以及生成结果。验收应比较元素数量、内容、索引边界和闭包调用规则,并覆盖项目支持的旧稳定工具链。完全限定的 trait 写法可以保持旧入口明确;只有确认新固有函数符合需求后,才移除旧扩展,不能只以编译通过作为迁移完成。