CodeBlanks Logo Icon CodeBlanks

GAT

Learn Generic Associated Types (GATs).

GATs

Generic associated types (GATs) let the associated type itself take a generic parameter (usually a lifetime). This way a trait can return a view type borrowing from self:

trait Bakilabilir {
    type Bakis<'a> where Self: 'a;
    fn bak<'a>(&'a self) -> Self::Bakis<'a>;
}

struct Kutu(Vec<i32>);
impl Bakilabilir for Kutu {
    type Bakis<'a> = &'a [i32];
    fn bak<'a>(&'a self) -> &'a [i32] { &self.0 }
}

GATs became stable with Rust 1.65; they make previously unwritable abstractions like "lending iterators" possible.

Different views

The same trait can return different borrowed views for different types:

struct Metin(String);
impl Bakilabilir for Metin {
    type Bakis<'a> = &'a str;
    fn bak<'a>(&'a self) -> &'a str { &self.0 }
}

let k = Kutu(vec![1, 2, 3]);
let m = Metin(String::from("merhaba"));
println!("{} {}", k.bak().len(), m.bak());   // 3 merhaba

Without GATs an associated type can't take a lifetime; you'd have to return a copy on each call or change the design.

Practice Borrowed views

Implement the Bakilabilir trait above for Kutu(Vec<i32>) and Metin(String). Print the results of Kutu(vec![1, 2, 3]).bak().len() and Metin("merhaba").bak() separated by a space.

Starter code

main.rs
fn main() {
    // buraya yaz
}

Sample solution

main.rs
trait Bakilabilir {
    type Bakis<'a>
    where
        Self: 'a;
    fn bak<'a>(&'a self) -> Self::Bakis<'a>;
}

struct Kutu(Vec<i32>);
struct Metin(String);

impl Bakilabilir for Kutu {
    type Bakis<'a> = &'a [i32];
    fn bak<'a>(&'a self) -> &'a [i32] {
        &self.0
    }
}

impl Bakilabilir for Metin {
    type Bakis<'a> = &'a str;
    fn bak<'a>(&'a self) -> &'a str {
        &self.0
    }
}

fn main() {
    let k = Kutu(vec![1, 2, 3]);
    let m = Metin(String::from("merhaba"));
    println!("{} {}", k.bak().len(), m.bak());
}

Expected output

3 merhaba

Questions and answers

Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.

1 Why is <code>where Self: 'a</code> needed?

  • It isn't needed
  • Since the view lives for 'a, Self must live at least that long too Correct answer
  • Convention
  • For speed

Explanation: The compiler requires this bound on GAT declarations, so the borrowed value can't outlive the source.

2 Associated type with a lifetime

Question
trait Bakilabilir {
    type Bakis____ where Self: 'a;
    fn bak<'a>(&'a self) -> Self::Bakis<'a>;
}
Answer
trait Bakilabilir {
    type Bakis<'a> where Self: 'a;
    fn bak<'a>(&'a self) -> Self::Bakis<'a>;
}

Explanation: A GAT adds a generic parameter to the associated type.

3 Implementation and Bakis type

Term Meaning
Kutu(Vec<i32>) &'a [i32]
Metin(String) &'a str
Iterator::Item Not a GAT
type Bakis<'a> GAT

All lessons in Advanced