---
# System prepended metadata

title: 'Design meeting 2021-10-13: Where the where'
tags: [design-meeting]

---

---
title: "Design meeting 2021-10-13: Where the where"
tags: "design meeting"
---

# Design meeting 2021-10-13: Where the where

Issue: https://github.com/rust-lang/rust/issues/89122
[Source](https://rust-lang.github.io/generic-associated-types-initiative/design-discussions/where-the-where.html)

First brought up on zulip: https://rust-lang.zulipchat.com/#narrow/stream/144729-wg-traits/topic/GAT.20syntax.20whining

## Summary

Proposed: to alter the syntax of where clauses on type aliases so that they appear *after* the value:

```
type StringMap<K> = BTreeMap<K, String>
where
    K: PartialOrd
```

This applies both in top-level modules and in traits (associated types, generic or otherwise).

## Background

The current syntax for where to place the "where clause" of a generic associated types is awkward. Consider this example ([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=bdb55a5d5cb17e20d73e22a3f2db0e57)):

```rust
trait Iterable {
    type Iter<'a> where Self: 'a;

    fn iter(&self) -> Self::Iter<'_>;
}

impl<T> Iterable for Vec<T> {
    type Iter<'a>
    where 
        Self: 'a = <&'a [T] as IntoIterator>::IntoIter;

    fn iter(&self) -> Self::Iter<'_> {
        self.iter()
    }
}
```

Note the impl. Most people expect the impl to be written as follows (indeed, the author wrote it this way in the first draft):

```rust
impl Iterable for Vec<T> {
    type Iter<'a>  = <&'a [T] as Iterator>::Iter
    where 
        Self: 'a;

    fn iter(&self) -> Self::Iter<'_> {
        self.iter()
    }
}
```

However, this placement of the where clause is in fact rather inconsistent, since the `= <&'a [T] as Iterator>::Iter` is in some sense the "body" of the item.

The same current syntax is used for where clauses on type aliases ([playground](https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=74eeed1795b693f238150f825a0e8438)):

```rust
type Foo<T> where T: Eq = Vec<T>;

fn main() { }
```

## Top-level type aliases

Currently, we accept where clauses in top-level type aliases, but they are deprecated (warning) and semi-ignored:

```
type StringMap<K> where
    K: PartialOrd
= BTreeMap<K, String>
```

Under this proposal, this syntax remains, but is deprecated. The newer syntax for type aliases (with `where` coming after the type) would remain feature gated until such time as we enforce the expected semantics.

## Interaction with trait aliases

One thing discussed in the thread was the interaction with trait alias syntax. With trait aliases, the where clauses can appear in different positions relative to `=` with different meanings:

```rust
// To use this alias, `T: Bar` must hold
trait Foo<T> where T: Bar = ...

// `Foo<T>` is an alias for `T: Bar`
trait Foo<T> = ... where T: Bar
```


However, this is related to another point about GAT syntax:

```rust
trait Foo {
    // To use Bar, T: Ord must hold
    type Bar<T: Ord>;
    
    // Impl must prove that `Self::Bar<T>: Ord`,
    // and other users can rely on that
    type Bar<T>: Ord;
}
```

There is no syntax with GATs today to add "impl must prove" constraints like `T: Ord`. Perhaps if there were, it might apply to trait aliases, too?

## Alternatives

### Keep the current syntax.

In this case, we must settle the question of how we expect it to be formatted (surely not as I have shown it above).

```rust
impl<T> Iterable for Vec<T> {
    type Iter<'a> where Self: 'a 
        = <&'a [T] as IntoIterator>::IntoIter;

    fn iter(&self) -> Self::Iter<'_> {
        self.iter()
    }
}
```

### Accept either

What do we do if both are supplied?

### Questions / Discussion

### Q

> Mark: I'm not sure I understand the distinction between the following two (from the trait alias section). Isn't it true in both that the `T: Bar` bound must hold?
> * is there a concise explanation somewhere for this? It feels like a critical element but also not something obvious (to me, anyway).


```rust
trait SendSync: Send + Sync
```

What about something more complex? [RFC #1733](https://rust-lang.github.io/rfcs/1733-trait-alias.html) gave this syntax:

```rust
trait RevPartialEq<T> = where T: PartialEq<Self>;
```

To illustrate difference:

```rust
// OK
trait RevPartialEq<T> = where T: PartialEq<Self>;
fn test<X: RevPartialEq<u32>>(x: X) {
    22_u32 == x
}
```

```rust
// Error: `u32: PartialEq<T>` does not hold
//
// Related to implied bounds, though.
trait Test<T> where T: PartialEq<Self> = Debug;
fn test<X: Test<u32>>(x: u32) {
    22_u32 == x
}
```

just as if I had written:

```rust
// just as if I had written:
//
// trait Test<T>
// where T: PartialEq<Self>
// { }
```

```rust
trait Test<T>
where Self: PartialEq<T>
{ }

// No error: `Self: PartialEq<u32>` is considerd a "super trait"
// and hence part of "implied bounds".
fn test<X: Test<u32>>(x: u32) {
    22_u32 == x
}
```

No way to make an implied bound that doesn't begin with Self.

```rust
trait DebugIterator {
    type Item: Debug;
    // Knowing that X: DebugIterator implies X::Item: Debug
}
```

No way to make an implied bound that doesn't begin with Self.

```rust
trait DebugIterator {
    type Item<T>: Debug;
    // What if I wanted `T: Debug<U>` as an implied bound?
    // Does that even make sense??
}
```

Niko: I feel the trait alias syntax is just confusing

Mark: Is there a reason to have both kinds of where clauses in traits? What if you only had implied bounds with trait aliases? (Or only the reverse...)

Felix: Does it have any impact on quality of dev ex?
    * Maybe you get an error that points to a better line of code?
    
We don't know.

Jack: As a data point, there was at least one issue with GATs where they were using where clauses but shouldn't have been (or maybe it was the other way around...) but I don't know what the right syntax would be.

(Update: I think this was the issue: https://github.com/rust-lang/rust/issues/87831)

Niko: I remember this, I think they were putting the where clause on the trait maybe?

### Q

> Felix: is part of expectation here that `cargo fix` or `rustfmt` will auto-convert the ugly syntax to the nice one? (This ties into my Q above regarding the increase in severity for parametric type aliases.) ((or maybe type aliases would be excluded from auto-suggested change))

niko: cargo fix for sure. For rustfmt, if we said that it was an error in GATs, then it would be new ground, right? Accepting a superset of rust and emitting rust? But I don't see any fundamental reason why not.

pnkfelix: if you convert type aliases, you are going from a where clause that gets ignored to one that gets enforced.

niko: yeah we'd need some opt-in of some kind.

### Q

> * Felix: In the "accept either" option: I assume we will always have to accept the old syntax, at least in old editions.
>	* so it makes some sense to continue accepting either.
>	* but that does *not* mean we have to accept both in tandem on the same item.

That makes sense, at least for top-level type aliases.

### Q

> * Scott: Random thought: `WHERE` and `HAVING` being different in SQL is also confusing to people, so I don't know if different keywords would necessarily be better.  Probably still better than it being position-dependent, though.

Niko: egads I forgot what those mean

Scott: HAVING comes after the GROUP BY, and you can reference difference things; it applies to the results of your projection.

Niko: I kind of agree that unless the keywords are *very well chosen* it won't help.

Scott: Is there some way to leverage the same thing twice somehow to get the semantics, vs two different keywords?

Niko: Do people even want that distinction? When discussing implied bounds, there was discussion about how removing where clauses becomes semver significant. Almost a private vs a public bound. Do you get to rely on it, or does everyone else get to rely on it too?

### Q

> * Scott: we have `struct Foo<T>(T) where T: Copy;` but `struct Foo<T> where T: Copy { x: T }`.  Does anyone remember why those are different?  Is there a general rule there that we can divine from there that would help us pick here?  Something about braces?

Niko: I don't recall it being discussed a lot, but the general rule was that the first one looked like a function. [RFC 0135](https://rust-lang.github.io/rfcs/0135-where.html)

Scott: Seems like it has to do with braces enclosing a lot of content, but the parens were short... maybe that says that the type is "one thing", not a multi-line whatever, so it is more like the tuple struct case.

Niko: I feel that way, it feels more like the fn or tuple struct case to me.

Scott: It feels like it would be weird to have the where clause list before the parameter list or `->` in a function, although you could have it there if you wanted.

Niko: The original RFC argued that function parameters and return types were more important than the where clauses, and that the bounds were a kind of footnote. I think you can make an analogous case here.

Niko: Does anyone want to argue *against* the proposed where clause syntax?

Scott: I'm in favor, but I'm trying to figure out *why*.

Jack: It's very subjective. Everybody seems to agree that the where clauses should go at the end because of formatting, but it's very subjective.

Arguments for the current syntax (where before =):

* Consistency of 'copying and pasting' an item from trait and appending 'the definition' (which in this is the value of the type alias).
* Trait alias subtleties.

Arguments against (iow, for where after =):

* Consistency with function and tuple struct placement
    * Note that we initially had tuple structs put the where before the `()` ([link](https://github.com/rust-lang/rust/issues/17904#issuecomment-58603749)) but it was "obviously wrong" somehow
* The thing after the where clauses (if any) should start on its own line, and we don't have precedent for `=` starting on its own line
    * where clauses are a "big long list" of things and you want it to be easy to find the end
* Calls attention to the value of the type alias vs the bounds
    * can put them in the angle brackets to emphasize them (most of the time)
    * ["secondary notation"](https://en.wikipedia.org/wiki/Cognitive_dimensions_of_notations) works better, more able to draw a distinction

There's a reason that rustfmt behaves like so:

```rust
let x = long_expression;

// Becomes
let x =
    long_expression;

// Not
let x
    = long_expression;
```


Example of issue with "long" where clauses: https://github.com/rust-lang/rust/issues/86787

```rust
    type T = Either<Left::T, Right::T>;
    type TRef<'a>
    where 
        <Left as HasChildrenOf>::T: 'a,
        <Right as HasChildrenOf>::T: 'a
    = Either<&'a Left::T, &'a Right::T>;

    type T = Either<Left::T, Right::T>;
    type TRef<'a>
    where 
        <Left as HasChildrenOf>::T: 'a,
        <Right as HasChildrenOf>::T: 'a
        = Either<&'a Left::T, &'a Right::T>;

    type T = Either<Left::T, Right::T>;
    type TRef<'a> = Either<&'a Left::T, &'a Right::T>
    where 
        <Left as HasChildrenOf>::T: 'a,
        <Right as HasChildrenOf>::T: 'a;
    
    // Scott: but ending a multiline list with commas?
    // Niko: how is it different from tuple structs?
    
    struct Foo<T>(u32)
    where
        T: Ord;
        
    // Yup, checked, and rustfmt does
    struct Foo<T, U>(T, U)
    where
        T: Ord,
        U: Ord;
```

### Q

Would implied bounds even make sense on GATs?

```rust
trait MyTrait {
    type MyType<T> = where T: PartialEq<Self::MyType<T>>;
}

impl MyTrait for .. {
    type MyType<T> = ValueType where T: PartialEq<Self::MyType<T>>;
}

impl<A> PartialEq<ValueType> for A { }

```

It is true that

* if we do want a syntax for this
* and it is the trait alias syntax

we just messed it up.
- Comment from Jack - But actually the "implied bound" where clause is in front of the equals, and this is not that

### Q

> Scott: Do we need people to copy them over? Is it a breaking change to remove them? Does the impl need to repeat them?

Today they are implied, but we've also discussed this proposed change:

```rust
trait Foo {
    unsafe fn foo();
}

impl Foo for () {
    fn foo();
}

fn safe_fn() {
    <() as Foo>::foo(); // No unsafe needed
}
```

"If the compiler can identify your impl, it can use the signature for your impl" -- should that work just for unsafe, but not for where clauses? Seems weird.

Scott: What about lifetimes?

Niko: Currently everything is typed against the trait signature. I would expect that to be consistent with unsafe but it occurs to be that there is more of a circularity implementation wise.

* Today:
    * Consumers of the associated type would ignore the bounds on the impl, so they could be defaulted to the ones that appear in the trait.
* Tomorrow:
    * But if we made it significant that you use fewer, that would be a change, and in particular having an empty list would make code stop compiling if those predicates were required for the value to be well-typed.

But of course we could do this over an edition easily enough.

