> For the complete Mojo documentation index, see [llms.txt](/llms.txt).
> Markdown versions of all pages are available by appending .md to any URL (e.g. /docs/manual/basics.md).

# Generics

Type generics let you write code once and use it across many types
without duplicating logic. You don't need separate implementations or
type checks for each case. Mojo generates specialized versions for each
type you use.

Most languages stop there, and their generics only parameterize over
types. Mojo also supports value generics. Its parameter system accepts
both types and compile-time values using the same `[]` syntax.

Mojo distinguishes compile-time parameters from runtime arguments in its
syntax. Parameters go in square brackets `[]` and resolve at compile
time. Arguments go in parentheses `()` and resolve at runtime.

You see this distinction at every definition and call site, so you always
know what the compiler specializes and what gets passed at runtime.

```mojo
# T is a type parameter, threshold is a value parameter
# Both are compile-time. values is a runtime argument.
def count_above[
    T: Comparable & Copyable, threshold: T
](https://mojolang.org/docs/manual/values: List[T].md) -> Int:
    var count = 0
    for v in values:
        if v > threshold:
            count += 1
    return count
```

Many generics use traits to constrain which types work with the code.
A trait defines what a type must do, and generic code declares which
traits it requires.

The compiler enforces these requirements and generates specialized code
for each concrete type at the call site.

## The value of generics

Generics eliminate duplicated logic. You write one implementation that
adapts at compile time to the types and values you use, including types
from libraries you didn't write.

Type generics let one function, method, or struct work across many
types. You declare requirements once using trait conformance, and the
compiler enforces them. You avoid near-identical implementations and fix
bugs in one place.

Value generics let you parameterize over compile-time constants such as
sizes, thresholds, or feature flags. The compiler can specialize code
paths, remove dead branches, and optimize for specific values without
runtime cost.

Together, they let you express intent once and apply it broadly without
sacrificing correctness, clarity, or performance.

:::note
The compiler generates specialized code for each combination of
concrete types and values at call sites. This enables static checking
and optimization, but increases compile time and code size.
:::

## Type generics

Type generics let you write code that works across many types. You define
behavior once, and the compiler specializes it for each concrete type at
the call site.

### Setting generic parameter constraint annotations

_Constraints_ define what a type must do. You express the annotations
as traits or trait compositions. These annotations use trait requirements
to restrict which types your generic code accepts.

You must always constrain generic parameters. Traits define the
operations and guarantees your code relies on. They also expose associated
types (compile-time aliases) that your implementation can use. With these
constraints in place, the compiler can check correctness and report
missing requirements early.

- The most permissive conformance is
  [`AnyType`](/docs/std/builtin/anytype/AnyType/). It places no behavioral
  requirements on a type and serves as the least restrictive option.

- [`ImplicitlyDestructible`](/docs/std/builtin/anytype/ImplicitlyDestructible/)
  is a common baseline for types with lifetimes. It marks types that need
  cleanup when they go out of scope. Generic code that stores or owns
  values often requires it.

Without a fixed set of required features, generic code can't do meaningful
work. The compiler has no guarantees about what operations are valid.

Examples on this page use trait composition (such as
`Equatable & Copyable`) to declare the exact capabilities an algorithm
needs. Each trait defines required API elements that concrete types must
provide at the call site.

## Basic generics: compare two lists

Consider comparing two lists to test whether they contain the same values
in the same order.

You could write a separate implementation for each element type. Or you
can write one generic function that works for any list whose elements
support the operations you need.

This concrete version only works with integers:

```mojo
def all_equal_int(ref lhs: List[Int], ref rhs: List[Int]) -> Bool:
    if len(lhs) != len(rhs): return False

    for left, right in zip(lhs, rhs):
        if left != right:
            return False
    return True
```

The generic version doesn't care about the element type. It only requires
the capabilities the algorithm uses: elements must support equality
comparison and be copyable.

With those requirements, this updated function walks both lists element by
element, just like the integer version:

```mojo
def all_equal[
    T: Equatable & Copyable
](https://mojolang.org/docs/manual/ref lhs: List[T], ref rhs: List[T].md) -> Bool:
    if len(lhs) != len(rhs): return False

    for left, right in zip(lhs, rhs):
        if left != right:
            return False
    return True
```

Both implementations follow the same logic: check lengths, return `False`
on the first mismatch, and return `True` if no differences are found.

### How this works

The generic pieces in this example make a function reusable across
many types. The first sign is the type parameter `T`. You declare type
parameters in square brackets before the function arguments. Here, `T`
represents the element type for both lists.

:::note
Mojo favors names that show purpose over type. It follows naming
conventions used in languages like Rust and C++, which keeps generic
signatures readable.

Type parameter names use PascalCase. They can be short (`T`, `E`) or
descriptive (`ErrorType`, `Element`). Common conventions:

- `T`, `U`, `V` general type parameters
- `K`, `V` key and value types
- `H` hasher types or functions
- `E` error types, as in `Result[T, E]`

Value parameter names use lower_snake_case. They should be descriptive
(`capacity`, `hasher`, `value`, `writer`, `tile_x`).
:::

When you call `all_equal()`, the compiler infers `T` from the call
site:

```mojo
print("Int (Expect True):\t",
    all_equal([1, 2, 3], [1, 2, 3])) # True
print("Int (Expect False):\t",
    all_equal([1, 2, 3], [4, 5, 6])) # False
print("String (Expect True):\t",
    all_equal(["hello", "world"], ["hello", "world"])) # True
print("String (Expect False):\t",
    all_equal(["hello", "world"], ["goodbye", "world"])) # False
```

The compiler generates a concrete, type-specific version of
`all_equal()` for each type you use. In this example, it creates one
for `Int` and one for `String`.

:::note
In type theory terms, the parametric all_equal() function is
_polymorphic_. The generated type-specific versions are _monomorphic_.
:::

### Type requirements

`Equatable` is required by this algorithm. `Copyable` is often needed
for loops and container elements. The compiler enforces both, so you
don't have to guess.

Keep requirements minimal:

```mojo
T: Equatable & Copyable
```

The ampersand (`&`) composes traits. It means both requirements apply.

Use the fewest constraints your code needs. That keeps your function usable
with more types. For example, if you remove `Equatable` from
`all_equal()`, the code fails to compile because the compiler can't
guarantee that `!=` exists for all `T`.

:::note
A type supports `!=` by implementing `__ne__()`.
:::

When your code uses an operation not covered by the constraints, the
compiler reports an error. The type is _underspecified_. Required
behavior isn't guaranteed by the current constraints.

Fix this by adding the trait that provides the missing behavior. Use
trait composition to extend the constraints.

Adding constraints restricts which types you accept, but expands what your
code can do. Each trait adds guaranteed operations, which lets the
compiler check correctness and reason about lifetimes and effects.

In practice, generic errors mean your constraints don't include the
behavior your code uses. Add the missing trait.

:::note
Constraints make your code sound: every operation you use is guaranteed to
exist for any type that satisfies them.
:::

### Generic parameter types

In this example, both parameters are `List`s that use the
same element type:

```mojo
lhs: List[T], rhs: List[T]
```

Using `T` for both arguments ensures your loop compares
like-with-like and prevents type mismatches.

The previous example showed lists containing elements
of type `T`. Generic types can also be used without embedding
them into container types, like you see here:

```mojo
def my_generic_fn[T: AnyType](https://mojolang.org/docs/manual/value: T.md):
```

This function accepts any type, because its only limit is `AnyType`.
`AnyType` is the root of the trait hierarchy. It's the baseline
that all types conform to. Using `AnyType` means the value has no guaranteed
destructor or lifetime management. Outside of
[_reflection_](/docs/manual/metaprogramming/reflection/) (type introspection),
this function is effectively useless.

## Simplified conformance syntax with `Some`

You can replace explicit type parameters and conformances with concise
`Some[Trait(s)]` and `SomeTypeList[Trait(s)]` syntax in your
declarations. These forms support both a single trait and trait
compositions.

This syntax lets you conform types where you use them instead of declaring
a type parameter in one place and using it in another place.

For example:

```mojo
def my_generic_fn[T: Trait(s)](https://mojolang.org/docs/manual/value: T.md):
```

becomes:

```mojo
def my_generic_fn(value: Some[Trait(s)]):
```

You can use `Some` with any trait or trait composition, under any argument
convention, and in the code where you've been using type parameters. There
are no restrictions on whether a conformed type is inferred or explicit.
You can use any convention your arguments require. If the compiler
can't infer a concrete type, it errors and asks you to use explicit type
parameters instead.

### Arguments

You can call `__int__()` on `x` with either style. The `Some` version is
shorter and places the trait requirement directly on the argument:

```mojo
# Before
def foo[T: Intable, //](https://mojolang.org/docs/manual/x: T.md) -> Int:
    return x.__int__()

# After
def foo(x: Some[Intable]) -> Int:
    return x.__int__()
```

### Function types

Mojo supports `Some` with function types. This example moves a closure
parameter conformance to an argument `Some` type:

```mojo
# Before
def sync_parallelize[
    FuncType: def(Int) -> None,
](https://mojolang.org/docs/manual/func: FuncType.md):
    ...

# After
def sync_parallelize(func: Some[def(Int) -> None]):
    ...
```

### Variadics

This example uses `*SomeTypeList` to conform a variadic parameter pack
instead of a single type parameter:

```mojo
# Before
def show[*Ts: Writable](https://mojolang.org/docs/manual/*pack: *Ts.md):
    ...

# After
def show(*pack: *SomeTypeList[Writable]):
    ...
```

### Operator overloads

Operator overloads are a great use case for `Some` syntax because they
place the trait that enables the operator directly on the declaration.

```mojo
# Before
def __getitem__[I: Indexer, //](https://mojolang.org/docs/manual/self, idx: I.md) -> ref[self.x] Self.T:
    ...

# After
def __getitem__(self, idx: Some[Indexer]) -> ref[self.x] Self.T:
    ...
```

### Where `Some` won't work

`Some` cannot replace type parameters in all cases. In the following
example, Mojo cannot infer a concrete type from the context and errors:

```mojo
@fieldwise_init
struct Struct(Writable):
    var x: Some[Copyable & ImplicitlyDestructible & Writable]
    # comptime[T: Copyable & ImplicitlyDestructible & Writable] T' is not
    # a concrete type, use '[]' to bind missing parameters
def main():
    var s = Struct(1)
    print(s)
```

Move the conformances back to a generic type parameter to fix this:

```mojo
@fieldwise_init
struct Struct[T: Copyable & ImplicitlyDestructible & Writable](https://mojolang.org/docs/manual/Writable.md):
    var x: Self.T

def main():
    var s = Struct(1)
    print(s)  # Struct[Int](https://mojolang.org/docs/manual/x=1.md)
```

## Generic types

So far, you've seen generics applied to methods and functions. Mojo
also lets you define generic types. These use compile-time parameters
to define both their fields and their methods.

Generic types let you package a reusable _shape_: stored fields plus
supported operations. Instead of coding `PairInt`, `PairString`, and so
on, you write one `Pair[T]` and let the compiler generate specialized
versions for each concrete `T` you use.

```mojo
comptime ComparableValue = Equatable & ImplicitlyCopyable

@fieldwise_init
struct Pair[T: ComparableValue](https://mojolang.org/docs/manual/ComparableValue.md):
    var left: Self.T
    var right: Self.T

    def __eq__(self, other: Pair[Self.T]) -> Bool:
        return self.left == other.left and
               self.right == other.right
```

Like generic functions, generic types use placeholder parameters. The
difference is that a generic type uses those parameters in its storage
as well as in its methods. Here, `Pair` stores two values of the same
element type and implements equality by comparing its fields.

Traits are what make generic types practical. Those conformances describe
the requirements the type definition depends on.
In this example, `Pair` needs to compare values,
so `T` must be equatable. It also needs to assign and copy values in common
operations, so it applies a trait composition conformance on `T`:

```mojo
comptime ComparableValue = Equatable & ImplicitlyCopyable
```

The `Pair` definition applies this conformance in two places: to the type
itself (in parentheses) and to the type parameter (in square brackets),
which is used to define its fields.

```mojo
struct Pair[T: ComparableValue](https://mojolang.org/docs/manual/ComparableValue.md):
```

- For the parentheses after the type, you can say the struct "conforms to"
  or "implements" this constraint. This is a promise.
  The `Pair` type is saying, "I am a `ComparableValue`."
- For the square brackets, the conformance restricts which values
  callers can pass. This is a requirement: "Any value used with `Pair`
  must have type `T`, and `T` must be a `ComparableValue`."

This makes `Pair[T]` usable anywhere a `ComparableValue` is required
(because of the parentheses) and ensures all `T` values conform (because
of the square brackets).

So far, these examples use type parameters. Generic types can also take
non-type parameters, which you'll see next.

## Mixing type and value parameters

Generics don't limit what parameters you can use. Add whatever you
need to define the behavior.

```mojo
struct ExampleStruct:
    def example[
        T: Writable & Copyable,   # type parameter
        count: Int,               # value parameter
    ](https://mojolang.org/docs/manual/self,
        data: String,             .md# argument
        init_value: T             # generic argument) -> String:
```

By convention, Mojo uses lower_snake_case for compile-time value
parameters. This visually distinguishes them from type parameters.

## Downcasting safely: `conforms_to` + `trait_downcast()`

Sometimes a generic conformance is broader than what you need. You may want
operations from a specific trait that the conformance doesn't guarantee. Use
a guarded downcast with `conforms_to()` and `trait_downcast()`.

Downcast only when the underlying type implements the target trait.
Check conformance first. Then downcast and use the value through that
trait.

This pattern keeps behavior explicit and safe:

```mojo
def process[T: AnyType](https://mojolang.org/docs/manual/value: T.md):
    comptime if conforms_to(T, Writable & ImplicitlyCopyable):
        var w = trait_downcast[Writable & ImplicitlyCopyable](https://mojolang.org/docs/manual/value.md)
        print(w)
    else:
        print("<not writable>")
        # Or fail at compile time with `comptime assert`
```

Key points:

- `conforms_to(T, Writable & ImplicitlyCopyable)` checks whether `T`
  satisfies the trait composition.
- `trait_downcast()` rebinds the value to a trait-constrained view at
  compile time.

Only call `trait_downcast()` inside the guarded branch. Handle the
fallback in the `else` branch. If you always need the trait, add it as
a constraint (for example, `T: Writable`) instead of downcasting.

Use this pattern when you can't express the requirement as a constraint.
It's common in reflection-style code that handles values based on
their traits. See [reflection](/docs/manual/metaprogramming/reflection/).

## Type generics and explicit destruction

Explicitly destroyed types don't always work with generic code. The
issue isn't generics, it's lifetime management. Explicit destruction
gives you control over teardown. You can define destructors that take
arguments, follow different paths, or raise errors.

Generic code that copies or moves values can't see or honor that
logic. Once a value is copied or transferred, you lose control over
how (or whether) it's cleaned up, and the code won't compile.

Watch for these cases:

- Generic code that manages lifetimes
- Generic code that copies or transfers values

These show up most often in containers, collections, and iterators.
They copy values or take ownership and decide when destruction
happens.

Safe cases:

- Generic operations that don't affect lifetimes

Think comparisons, predicates, and other pure operations.

Rule of thumb: if your generic code needs to own, copy, or control
when a value dies, avoid explicitly destroyed types. Add an
`ImplicitlyDestructible` constraint to keep things working.

## Value generics

Value generics parameterize code over compile-time constants instead
of types. You declare them in `[]` alongside type parameters, but they
bind to values.

### When to use value generics

Use value generics when a value shapes structure or behavior and is
known at compile time. Common cases:

- _Fixed sizes:_ buffer lengths, array dimensions, matrix shapes
- _Thresholds and limits:_ capacity caps, retry counts, precision levels
- _Feature selection:_ algorithm variants, debug flags, mode switches
- _Numeric configuration:_ SIMD widths, stride lengths, unroll factors

The compiler specializes code for each distinct value. It can remove
dead branches (`comptime if`), unroll loops (`comptime for`), replace
inline constants, and optimize aggressively with no runtime cost.

### Basic example

This function creates a fixed-size list initialized with a default
value:

```mojo
comptime MyCollectionElement = ImplicitlyCopyable & ImplicitlyDestructible

def make_filled[T: MyCollectionElement, size: Int](https://mojolang.org/docs/manual/splat_value: T.md) -> List[T]:
    var result = List[T](https://mojolang.org/docs/manual/capacity=size.md)
    for _ in range(size):
        result.append(splat_value)
    return result^
```

The `size` parameter resolves at compile time. Each call site with a
different value gets its own specialized version:

```mojo
var three_zeros = make_filled[Int, 3](https://mojolang.org/docs/manual/0.md)
var five_hellos = make_filled[String, 5](https://mojolang.org/docs/manual/"hello".md)
print(three_zeros)  # [0, 0, 0]
print(five_hellos)  # [hello, hello, hello, hello, hello]
```

### Value parameters vs runtime arguments

Put values known at compile time in `[]`. Put values only known at
runtime in `()`.

Ask yourself: does the caller know this value when writing the code? If
yes, use a parameter. If it depends on input, files, or runtime state,
use an argument.

```mojo
# size is compile-time: the compiler specializes
def fixed[size: Int]():
    var buf = InlineArray[Int, size](https://mojolang.org/docs/manual/fill=0.md)

# size is runtime: no specialization
def dynamic(size: Int):
    var buf = List[Int](https://mojolang.org/docs/manual/capacity=size.md)
```

## When generics get more complex

The examples on this page cover the most common cases. As you build
larger APIs, you'll run into a few additional patterns:

- _Associated (dependent) types:_
  Some traits declare
  [associated types](/docs/manual/traits/#comptime-members-for-generics).
  For example, collection and iteration traits use an associated
  element type (often `Element`) to describe the values they own.
  Associated types can also constrain behavior—for example, requiring
  elements to be hashable or implicitly destructible.

- _Generic methods in non-generic types:_
  A type doesn't have to be generic to use generics. Individual methods
  can declare their own type parameters when only part of the API needs
  them.

- _Multiple type parameters:_
  Some algorithms use more than one type parameter. Traits describe how
  those types relate, not just what each can do. Common examples include
  dictionaries (keys and values), caches (keys and values), result types
  (values and errors), and algorithms that operate across multiple types.

- _Compile-time computation:_
  Value parameters enable `comptime if`, `comptime for`, and
  compile-time arithmetic. This shows up in SIMD code, fixed-size
  containers, and performance-critical loops where the compiler
  generates different code paths for different values.

These topics build on the same core ideas: type parameters, constraints,
and specialization. You can apply constraints to functions, methods,
types, or `comptime` aliases to keep requirements close to where they
matter.

You won't need these patterns for simple cases, but they matter as
generic code grows.

## Conditional trait conformance

:::caution
Conditional trait conformance is new and evolving. Certain
capabilities may be unstable or unusable during roll-out. Use
caution with `Copyable`, `Movable`, `RegisterPassable`,
`TrivialRegisterPassable`, and `ImplicitlyDestructible`.
:::

_Conditional trait conformance_ uses checks before allowing a type to
adopt a trait. If the condition is _satisfied_, the type conforms.
It must fulfill the trait's requirements by providing required methods
and associated types, and it gains any default implementation provided
by the trait. The type can then be used anywhere the trait is required,
such as when passing it to a function that expects a conforming type.

When the condition _isn't_ satisfied, Mojo skips the conformance. You
can't use the fully concrete type in contexts that require the trait.

### Example: derived conformance

In the following declaration, Mojo conforms `Wrapper` to `Writable`
when its parameter, `T`, is also `Writable`:

```mojo
comptime BaseTraits = Copyable & ImplicitlyDestructible
@fieldwise_init
struct Wrapper[T: BaseTraits](https://mojolang.org/docs/manual/Writable where conforms_to(T, Writable.md)
):
    var value: Self.T
```

When conforming to `Writable`, Wrapper doesn't need to implement any
methods to enable printing. The trait provides a default implementation
of the `write_to()` method.

Now consider a type that isn't `Writable`, that you might add into a
`Wrapper`:

```mojo
@fieldwise_init
struct NotWritable(BaseTraits):
    var data: Int
```

When instantiated with an `Int` or `String` (both `Writable`), the
`Wrapper` gains `Writable` conformance. With `NotWritable`, you can build
a struct, but you can't print it:

```mojo
var w_int = Wrapper[Int](https://mojolang.org/docs/manual/42.md)  # Int is Writable
print(w_int)  # Wrapper[Int](https://mojolang.org/docs/manual/value=42.md)

var w_str = Wrapper[String](https://mojolang.org/docs/manual/"Hello".md)  # String is Writable
print(w_str)  # Wrapper[String](https://mojolang.org/docs/manual/value=Hello.md)

# OK: only `Writable` conformance is unavailable
var w_not_writable = Wrapper[NotWritable](https://mojolang.org/docs/manual/NotWritable(10.md))
# print(w_not_writable)  # Compile-time error:
# invalid call to 'print': could not convert element of 'values' with
# type 'Wrapper[NotWritable]' to expected type 'Writable'
```

Since `NotWritable` doesn't conform to `Writable`, the conditional
conformance check fails and `Wrapper[NotWritable]` doesn't adopt the
`Writable` conformance. In contrast, `Int` and `String` are `Writable`
and their wrappers gain the conformance. This allows their instances
to print by using the `write_to()` method provided by `Writable`.

This pattern is standard for single-type containers like `Optional[T]`,
`Box[T]`, `Lazy[T]`, and `List[T]`. It says: "This type can do X if its inner
type can do X."

### Example: parts conformance

Conditional conformance has another standard pattern for types with
multiple distinct components: `Result[T, E]`, `Pair[L, R]`, `Dict[K, V]`,
and similar. It says: "This type can do X if each of its parts can do X":

```mojo
comptime BaseTraits = Copyable & ImplicitlyDestructible

@fieldwise_init
struct Pair[L: BaseTraits, R: BaseTraits](https://mojolang.org/docs/manual/Hashable where conforms_to(L, Hashable.md) and conforms_to(R, Hashable)
):
    var left: Self.L
    var right: Self.R

@fieldwise_init
struct NotHashable(BaseTraits):
    var data: Int
```

In this example, `Pair` uses two distinct type parameters. When both are
`Hashable`, the concrete `Pair` type becomes `Hashable`, gaining the `hash()`
method from the trait:

```mojo
var pair = Pair[Int, String](https://mojolang.org/docs/manual/left=1, right="one".md)
var hash = hash(pair)
print(hash)  # Prints the hash of the pair

# OK: only hashing is unavailable
var pair2 = Pair[Int, NotHashable](https://mojolang.org/docs/manual/left=1, right=NotHashable(10.md)) # Constructible
# var hash2 = hash(pair2) # Compile time error
```

### Example: conditional method access

When a type adopts a trait, it must satisfy the trait's required
methods. The same conditions that determine whether a type can
conditionally conform to a trait are usually the same conditions used
to _gate_ required method implementations.

You express these method tests with `where` clauses. If true, the
method is available. If not, the method can't be used for that
specialized type. The following example walks you through this
process and how it works.

You can use `Boolable` instances in `if` statements. You make your
`Wrapper` testable by conforming to the `Boolable` trait. This
trait requires a new method, `__bool__()`:

``` mojo
@fieldwise_init
struct Wrapper[T: BaseTraits](https://mojolang.org/docs/manual/Writable where conforms_to(T, Writable.md),
    Boolable where conforms_to(T, Boolable), # New conditional conformance
    ):
    var value: Self.T

    # New method
    def __bool__(self) -> Bool where conforms_to(Self.T, Boolable):
        return trait_downcast[Boolable](https://mojolang.org/docs/manual/self.value).__bool__()
```

In this example, the condition on `__bool__()` is the same as the one
used for the `Boolable` conditional conformance. The `where` clause gates
the method. If the condition is true, the method is available. If not,
it isn't.

Since the method and the conformance use the same condition, they
stay aligned. You won't end up in a situation where the wrapped type
conforms but the method isn't available, or where the method exists
without the corresponding conformance.

You see this in the following examples:

```mojo
var w_str = Wrapper[String](https://mojolang.org/docs/manual/"Hello".md)
if w_str:  # Chooses the non-empty branch
    print(t"Non-empty string \"{w_str.value}\" is truthy")
else:
    print(t"Empty string \"{w_str.value}\" is falsy")

var w_empty_str = Wrapper[String](https://mojolang.org/docs/manual/"".md)
if w_empty_str:  # Chooses the empty branch
    print(t"Non-empty string \"{w_empty_str.value}\" is truthy")
else:
    print(t"Empty string \"{w_empty_str.value}\" is falsy")
```

The `NotWritable` type from earlier in this section has no traits
beyond the base `Copyable` and `ImplicitlyDestructible`. Therefore
it isn't `Boolable` and the condition on the `__bool__()` method
will fail:

```mojo
@fieldwise_init
struct NotWritable(BaseTraits):
    var data: Int

var w_not_writable = Wrapper[NotWritable](https://mojolang.org/docs/manual/NotWritable(10.md))

# The instance can't be used as a boolean value due to the
# conditional method access for `__bool__()`.

# Compiler error: the method condition is false
if w_not_writable:
    print(t"NotWritable with data {w_not_writable.value.data} is truthy")
else:
    print(t"NotWritable with data {w_not_writable.value.data} is falsy")
```

This condition on your method isn't conditional trait conformance.
It's conditional method _access_. In this example, the method and the
conformance use the same condition, so they stay aligned.

That said, `where` clauses on methods and functions are useful even when
you're not working with trait conformance. For example, you might gate
a method so it only works with non-empty lists, real numbers, or with
values that fall within the index bounds of a collection.

### Conditional trait composition

Mojo supports flexible condition composition, as shown in these examples:

- Unconditional: No special clauses.

  ```mojo
  struct Foo(Copyable, ImplicitlyDestructible):
  ```

- Simple condition: as shown in `Wrapper`.

  ```mojo
  struct Wrapper[T: BaseTraits](https://mojolang.org/docs/manual/Writable where conforms_to(T, Writable.md)
  ):
  ```

- Hybrid: Mixes unconditional and conditional traits.

  ```mojo
  struct Foo[T: AnyType](https://mojolang.org/docs/manual/Copyable, Writable where conforms_to(T, Writable.md)
  )
  ```

- Multiple aligned conditions: as shown in `Pair`.

  ```mojo
  struct Pair[L: BaseTraits, R: BaseTraits](https://mojolang.org/docs/manual/Hashable where conforms_to(L, Hashable.md) and conforms_to(R, Hashable)
  ):
  ```

- Multiple independent conditions:

  ```mojo
  struct Foo[T: AnyType](https://mojolang.org/docs/manual/Writable where conforms_to(T, Writable.md),
    Hashable where conforms_to(T, Hashable),
  )
  ```

### Conditional conformance with value parameters

Conditional conformance also works with value parameters. You
can gate both conformance and methods on compile-time value
conditions.

This is useful when a type's capabilities depend on a numeric
parameter. For example, a fixed-capacity wrapper might only be
`Writable` when it has capacity and its elements are writable.
The `Sized` conformance is unconditional.

```mojo
struct SizedListWrapper[capacity: Int, T: Copyable](https://mojolang.org/docs/manual/Sized, Writable where conforms_to(T, Writable.md) and capacity > 0
):
    var data: List[Self.T]

    def __init__(out self, value: Self.T):
        self.data = List[Self.T](https://mojolang.org/docs/manual/capacity=Self.capacity)
        for _ in range(Self.capacity):
            self.data.append(value.copy())

    def __len__(self) -> Int:
        return len(self.data)

    def write_to(self, mut writer: Some[Writer]):
        writer.write(repr(self.data))
```

You can gate methods on value conditions:

```mojo
def first(self) -> Self.T where Self.capacity > 0:
    return self.data[0].copy()
```

When capacity is one or more, the type conforms to `Writable` and the
`first()` method is available. When it is zero or less, neither is
usable:

```mojo
var s = SizedListWrapper[5, Int](https://mojolang.org/docs/manual/42.md)
print(s)          # List[Int](https://mojolang.org/docs/manual/[Int(42.md), Int(42), Int(42), Int(42), Int(42)])
print(s.first())  # 42

# var s = SizedListWrapper[0, Int](https://mojolang.org/docs/manual/42.md)
# print(s)          # Error: Writable not satisfied
# print(s.first())  # Error: constraint is false
```

Value conditions follow the same rules as type conditions. You can
combine them with `and` and mix them with `conforms_to` checks in the
same `where` clause.
