Skip to content

Commit 8af6cca

Browse files
committed
default type parameters
Describe default types for generic parameters, as currently implemented. There are some differences from RFC 213, but I couldn't find any real specification, so relied on observed behavior. fixes #24
1 parent 4f86eeb commit 8af6cca

1 file changed

Lines changed: 108 additions & 1 deletion

File tree

src/items/generics.md

Lines changed: 108 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,9 @@ GenericParam -> OuterAttribute* ( LifetimeParam | TypeParam | ConstParam )
99
1010
LifetimeParam -> Lifetime ( `:` LifetimeBounds )?
1111
12-
TypeParam -> IDENTIFIER ( `:` TypeParamBounds? )? ( `=` Type )?
12+
TypeParam -> IDENTIFIER ( `:` TypeParamBounds? )? DefaultType?
13+
14+
DefaultType -> `=` Type
1315
1416
ConstParam ->
1517
`const` IDENTIFIER `:` Type
@@ -257,6 +259,110 @@ where
257259
}
258260
```
259261

262+
r[items.generic.defaulttypes]
263+
## Default types
264+
265+
r[items.generic.defaulttypes.intro]
266+
Generic type parameters may have default types, using the [DefaultType] syntax. Only type parameters on nominal types and traits may have default types.
267+
268+
Defaulted type parameters behave differently in expression and type contexts.
269+
270+
r[items.generic.defaulttypes.typectx]
271+
In type contexts, all non-defaulted type arguments are required, but may be specified as the inferred type `_`. Omitted defaulted type arguments will use the provided default.
272+
273+
```rust
274+
#[derive(Default)]
275+
struct S1<T=u8>(T);
276+
#[derive(Default)]
277+
struct S2<T, U=u8>(T, U);
278+
// T defaults to u8: QualifiedTypeInPath is type context
279+
let x = <S1>::default();
280+
// T defaults to u8: let Type is type context
281+
let y: S1 = S1::default();
282+
// U defaults to u8: T is inferred from z2
283+
let z1: S2<_> = S2::default();
284+
let z2: S2<u8, _> = z1;
285+
```
286+
287+
r[items.generic.defaulttypes.exprctx]
288+
In expression contexts, all omitted type arguments are treated as the inferred type `_`. [RFC 213] states that the provided default type will be used as a fallback during inference, but this seems work inconsistently in practice.
289+
290+
```rust
291+
#[derive(Debug, Default)]
292+
struct S<T=u8>(T);
293+
trait Tr { fn foo(&self); }
294+
impl Tr for S<u8> {
295+
fn foo(&self) {
296+
println!("in u8: {:?}", self);
297+
}
298+
}
299+
impl Tr for S<i32> {
300+
fn foo(&self) {
301+
println!("in i32: {:?}", self);
302+
}
303+
}
304+
// unexpectedly i32, due to interaction with integer fallback
305+
let x = S(1);
306+
x.foo();
307+
// u8, due to type annotation on let
308+
let y: S = S(2);
309+
y.foo();
310+
// unexpectedly i32, due to interaction with integer fallback
311+
S(3).foo();
312+
313+
// errors due to inference failure
314+
// S::default().foo();
315+
```
316+
317+
r[items.generic.defaulttypes.add]
318+
Default type parameters are useful on traits. For example, this is the `Add` trait in the standard library that allows customization of the `+` operator.
319+
320+
```rust
321+
trait Add<Rhs=Self> {
322+
type Output;
323+
324+
fn add(self, rhs: Rhs) -> Self::Output;
325+
}
326+
```
327+
328+
Many users will not need the added flexibility of specifying a different `Rhs` type for `Add`, but it can be useful. For example, `S1` and `S2` wrap `i32` into distinct types. A custom `Add` implementation allows us to add `S1` to `S2` with some extra behavior.
329+
330+
```rust
331+
# use std::ops::Add;
332+
#[derive(Debug)]
333+
struct S1(i32);
334+
#[derive(Debug)]
335+
struct S2(i32);
336+
impl Add for S1 {
337+
type Output = S1;
338+
fn add(self, rhs: S1) -> S1 {
339+
println!("in S1+S1");
340+
let (S1(x), S1(y)) = (self, rhs);
341+
S1(x + y)
342+
}
343+
}
344+
impl Add<S2> for S1 {
345+
type Output = S1;
346+
fn add(self, rhs: S2) -> S1 {
347+
println!("in S1+S2");
348+
let (S1(x), S2(y)) = (self, rhs);
349+
S1(x + 10 * y)
350+
}
351+
}
352+
println!("{:?}", S1(1) + S1(2));
353+
println!("{:?}", S1(1) + S2(2));
354+
```
355+
356+
r[items.generics.defaulttypes.forbidden]
357+
Default types are not allowed on implementations or functions.
358+
359+
```rust,compile_fail
360+
struct S;
361+
trait Tr<T> { fn f(self) -> T; }
362+
impl<T:Default=u8> Tr<T> for S { fn f(self) -> T { T::default() } }
363+
fn foo<T=u8>(x: T) {}
364+
```
365+
260366
r[items.generics.attributes]
261367
## Attributes
262368

@@ -300,6 +406,7 @@ struct Foo<#[my_flexible_clone(unbounded)] H> {
300406
[path expression]: ../expressions/path-expr.md
301407
[raw pointers]: ../types/pointer.md#raw-pointers-const-and-mut
302408
[references]: ../types/pointer.md#shared-references-
409+
[RFC 213]: https://rust-lang.github.io/rfcs/0213-defaulted-type-params.html
303410
[structs]: structs.md
304411
[tuples]: ../types/tuple.md
305412
[trait object]: ../types/trait-object.md

0 commit comments

Comments
 (0)