Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Parameterize a class

A generic class shares one declaration across concrete field types. Its methods can use the class’s type parameters.

class Cell[T]:
    value: T

    def get(self) -> T:
        self.value

    def replace(self: &mut Cell[T], value: T) -> ():
        self.value = value

def read[T](cell: &Cell[T]) -> T:
    cell.get()

def main() -> Result[(), IoError]:
    mut count = Cell(7u8)
    count.replace(9)
    print(read(&count))?
    text = Cell("hello")
    print(text.get())?
    Ok(())
9
hello

Constructors infer their parameters: Cell(7u8) and Cell[u8](7) have the same type. With Cell(7), the unsuffixed integer defaults to i64.

This get method works for integers and immutable strings, which can be read from a shared field. An owned list needs an explicit copy or a reference: returning it would move it out of a borrowed field. A Cell can hold a list, but its get cannot be called:

class Cell[T]:
    value: T

    def get(self) -> T:
        self.value

def main() -> Result[(), AllocError | IoError]:
    numbers = Cell([1, 2]?)
    first = numbers.get()
    Ok(())
lesson.plenty:9:13: `Cell[list[i64]].get` copies `T`, but `list[i64]` cannot be copied
lesson.plenty:5:9: note: in instantiation `Cell[list[i64]].get`: cannot move out of a field

A generic method is checked once for each set of type arguments it is used with, not where it is written. Without the get call, the program is accepted. The error is reported at the line of your code that first required Cell[list[i64]].get, here the method call, and names the type argument that the method cannot accept. The note below it gives the line in the definition and the reason.

That wording follows the value: here self.value is declared as T. An unrelated list created inside the method does not become a T value just because the caller also chooses a list for T. For failures without a clear parameter origin, the compiler instead reports that the instance’s definition is rejected, followed by the operation’s error. See instance diagnostics for the attribution rules.

When you write a generic class for others to use, plenty --check --check-all-methods FILE also checks the methods your program never calls, for every instance it creates of a generic type it declares. The generics reference describes the check.