bigywater.tech
Rust · 44

Generic Associated Type

GAT 是近几年才稳定的特性,解决的是「关联类型需要带参数」的问题。这篇用最典型的场景 —— 借出式迭代器 —— 验证它:没有 GAT 时标准 Iterator 为什么做不到,有了 GAT 怎么写,以及它的另一种用法(带类型参数的容器族)。

type Item<'a>
GAT:关联类型自己带生命周期参数
[1, 3, 6, 10]
借出式迭代器逐窗口修改后的最终数据
where Self: 'a
定义带生命周期的 GAT 时必须写的约束

为什么标准 Iterator 不行

实测 · 本机执行输出
  源码:
      1 | struct Windows { buf: Vec<i32>, pos: usize }
      2 | impl Iterator for Windows {
      3 |     type Item = &mut [i32];
      4 |     fn next(&mut self) -> Option<Self::Item> { None }
      5 | }
      6 | fn main() {}
  编译:失败
    error: associated type `Iterator::Item` is declared without lifetime parameters, so using a borrowed type for them requires that lifetime to come from the implemented type
     --> <实验目录>/src/u44a.rs:3:17
      |
    3 |     type Item = &mut [i32];
      |                 ^
      |
    note: you can't create an `Iterator` that borrows each `Item` from itself, but you can instead create a new type that borrows your existing type and implement `Iterator` for that new type
     --> <实验目录>/src/u44a.rs:2:19
      |
    2 | impl Iterator for Windows {
      |                   ^^^^^^^
    
    error: aborting due to 1 previous error
  源码:
      1 | struct Windows { buf: Vec<i32>, pos: usize }
      2 | impl Iterator for Windows {
      3 |     type Item = &'static mut [i32];
      4 |     fn next(&mut self) -> Option<Self::Item> {
      5 |         if self.pos + 2 > self.buf.len() { return None; }
      6 |         let w = &mut self.buf[self.pos..self.pos + 2]; self.pos += 1; Some(w)
      7 |     }
      8 | }
      9 | fn main() {}
  编译:失败
    error: lifetime may not live long enough
     --> <实验目录>/src/u44b.rs:6:71
      |
    4 |     fn next(&mut self) -> Option<Self::Item> {
      |             - let's call the lifetime of this reference `'1`
    5 |         if self.pos + 2 > self.buf.len() { return None; }
    6 |         let w = &mut self.buf[self.pos..self.pos + 2]; self.pos += 1; Some(w)
      |                                                                       ^^^^^^^ returning this value requires that `'1` must outlive `'static`
    
    error: aborting due to 1 previous error

Iterator 的基础见Iterator Trait;关联类型的基础见Associated Type。

GAT 的借出式迭代器

实测 · 本机执行输出
  源码:
      1 | trait LendingIterator {
      2 |     type Item<'a> where Self: 'a;
      3 |     fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>;
      4 | }
      5 | struct WindowsMut<'s> { data: &'s mut [i32], pos: usize }
      6 | impl<'s> LendingIterator for WindowsMut<'s> {
      7 |     type Item<'a> = &'a mut [i32] where Self: 'a;
      8 |     fn next<'a>(&'a mut self) -> Option<&'a mut [i32]> {
      9 |         if self.pos + 2 > self.data.len() { return None; }
     10 |         let w = &mut self.data[self.pos..self.pos + 2]; self.pos += 1; Some(w)
     11 |     }
     12 | }
     13 | fn main() {
     14 |     let mut v = vec![1, 2, 3, 4];
     15 |     let mut it = WindowsMut { data: &mut v, pos: 0 };
     16 |     while let Some(w) = it.next() { w[1] += w[0]; println!("窗口 {:?}", w); }    // 相邻窗口重叠,每个窗口可修改
     17 |     println!("最终数据 {:?}", v);
     18 | }
  编译:成功
  运行(退出码 0):
    窗口 [1, 3]
    窗口 [3, 6]
    窗口 [6, 10]
    最终数据 [1, 3, 6, 10]

带类型参数的 GAT

实测 · 本机执行输出
  源码:
      1 | trait Family { type Of<T>; fn wrap<T>(t: T) -> Self::Of<T>; fn peek<T: std::fmt::Debug>(w: &Self::Of<T>) -> String; }
      2 | struct VecFamily; struct OptFamily;
      3 | impl Family for VecFamily { type Of<T> = Vec<T>; fn wrap<T>(t: T) -> Vec<T> { vec![t] } fn peek<T: std::fmt::Debug>(w: &Vec<T>) -> String { format!("{:?}", w) } }
      4 | impl Family for OptFamily { type Of<T> = Option<T>; fn wrap<T>(t: T) -> Option<T> { Some(t) } fn peek<T: std::fmt::Debug>(w: &Option<T>) -> String { format!("{:?}", w) } }
      5 | fn demo<F: Family>() -> (String, String) { (F::peek(&F::wrap(1u8)), F::peek(&F::wrap("x"))) }
      6 | fn main() { println!("VecFamily:{:?}", demo::<VecFamily>()); println!("OptFamily:{:?}", demo::<OptFamily>()); }
  编译:成功
  运行(退出码 0):
    VecFamily:("[1]", "[\"x\"]")
    OptFamily:("Some(1)", "Some(\"x\")")

where Self: 'a

实测 · 本机执行输出
  源码:
      1 | trait Lending { type Item<'a>; fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>; }
      2 | fn main() {}
  编译:失败
    error: missing required bound on `Item`
     --> <实验目录>/src/u44e.rs:1:17
      |
    1 | trait Lending { type Item<'a>; fn next<'a>(&'a mut self) -> Option<Self::Item<'a>>; }
      |                 ^^^^^^^^^^^^^-
      |                              |
      |                              help: add the required where clause: `where Self: 'a`
      |
      = note: this bound is currently required to ensure that impls have maximum flexibility
      = note: we are soliciting feedback, see issue #87479 <https://github.com/rust-lang/rust/issues/87479> for more information
    
    error: aborting due to 1 previous error

分步演示

GAT
本步的对象

    本文用到的工具与边界

    • 本机 macOS 27 arm64 / rustc 1.98.1(brew)/ cargo 1.98.1;单个文件用 rustc 直接编译,需要依赖时用 cargo(经代理从 crates.io 下载);计时用 std::time::Instant,数字每次运行会有小幅抖动。错误信息为原样的 rustc 1.98.1 输出
    • 没有验证:GAT 与 dyn Trait 的关系、GAT 在 HRTB(for<'a>)约束下的使用限制、标准库是否已有借出式迭代器的 trait(没有查)、与 async trait 的关系
    京ICP备2026067502号-1京公网安备11010502063487号