@@ -882,6 +882,7 @@ trait Foo<Input1, Input2> {
882
882
type Output1 ;
883
883
type Output2 ;
884
884
lifetime 'a ;
885
+ const C : bool ;
885
886
...
886
887
}
887
888
```
@@ -894,6 +895,7 @@ T: Foo<I1, I2, Output1 = O1>
894
895
T: Foo<I1, I2, Output2 = O2>
895
896
T: Foo<I1, I2, Output1 = O1, Output2 = O2>
896
897
T: Foo<I1, I2, Output1 = O1, 'a = 'b, Output2 = O2>
898
+ T: Foo<I1, I2, Output1 = O1, 'a = 'b, C = true, Output2 = O2>
897
899
```
898
900
899
901
The output constraints must come after all input arguments, but can appear in
@@ -954,20 +956,21 @@ trait Foo<Input1, Input2> {
954
956
type Output1 ;
955
957
type Output2 ;
956
958
lifetime 'a ;
959
+ const C : bool ;
957
960
...
958
961
}
959
962
```
960
963
961
964
Unlike the case for static trait bounds, which do not have to specify any of the
962
- associated types or lifetimes (but do have to specify the input types), trait
963
- object types must specify all of the types:
965
+ associated types, lifetimes, or consts, (but do have to specify the input types),
966
+ trait object types must specify all of the types:
964
967
965
968
``` rust
966
969
fn consume_foo <T : Foo <I1 , I2 >>(t : T ) // this is valid
967
970
fn consume_obj (t : Box <Foo <I1 , I2 >>) // this is NOT valid
968
971
969
972
// but this IS valid:
970
- fn consume_obj (t : Box <Foo <I1 , I2 , Output1 = O2 , Output2 = O2 , 'a = 'static >>)
973
+ fn consume_obj (t : Box <Foo <I1 , I2 , Output1 = O2 , Output2 = O2 , 'a = 'static , C = true >>)
971
974
```
972
975
973
976
With this design , it is clear that none of the non - `Self ` types are erased as
@@ -1199,70 +1202,10 @@ clauses, it is probably too much of a hack to be viable for use in `libstd`.
1199
1202
1200
1203
### Associated consts in generic code
1201
1204
1202
- There are some restrictions on uses of associated consts in generic code. These
1203
- might be loosened or removed in the future (see the related sub-sections in
1204
- "Unresolved questions" below).
1205
-
1206
- 1 . Values of constant expressions in match patterns cannot depend on a type
1207
- parameter (by extension, neither can the types of such expressions). This
1208
- restriction is necessary for exhaustiveness and reachability to be checked
1209
- in generic code.
1210
-
1211
- Note that the dependence of a value on a type parameter may be indirect:
1212
-
1213
- ``` rust
1214
- enum MyEnum {
1215
- Var1 ,
1216
- Var2 ,
1217
- }
1218
- trait HasVar {
1219
- const VAR : MyEnum ;
1220
- }
1221
- fn do_something <T : HasVar >(x : MyEnum ) {
1222
- const y : MyEnum = <T >:: VAR ;
1223
- // The following is forbidden because the value `y` depends on `T`.
1224
- match x {
1225
- y => { /* ... */ }
1226
- _ => { /* ... */ }
1227
- }
1228
- // However, this is OK because the guard is not a part of the pattern.
1229
- match x {
1230
- z if z == y => { /* ... */ }
1231
- _ => { /* ... */ }
1232
- }
1233
- }
1234
- ```
1235
-
1236
- 2 . Array sizes that depend on type parameters cannot be compared for equality
1237
- by type - checking , with one exception : if the expression for an array size
1238
- comprises only a single reference to a constant item (or associated item ),
1239
- it will be considered equal to any other array size that refers to the same
1240
- item , even if that item itself depends on the type parameters .
1241
-
1242
- For clarification , here are some examples . Assume that `T ` is a type
1243
- parameter in the outer scope , and that it is known to have an associated
1244
- const `<T >:: N ` of type `usize `.
1245
-
1246
- ```rust
1247
- // This is OK (but there are limitations to how x can be used).
1248
- let x : [u8 ; <T >:: N ] = [0u8 ; <T >:: N ];
1249
- // Equivalent to the above.
1250
- let x = [0u8 ; <T >:: N ];
1251
- // Neither of the following are allowed (type checking shouldn't have to
1252
- // know anything about arithmetic).
1253
- let x : [u8 ; 2 * <T >:: N ] = [0u8 ; <T >:: N + <T >:: N ];
1254
- let x : [u8 ; <T >:: N + 1 ] = [0u8 ; 1 + <T >:: N ];
1255
- // Still not allowed.
1256
- let x : [u8 ; <T >:: N + 1 ] = [0u8 ; <T >:: N + 1 ];
1257
- // Workaround for the expression above.
1258
- const N_PLUS_1 : usize = <T >:: N + 1 ;
1259
- let x : [u8 ; N_PLUS_1 ] = [0u8 ; N_PLUS_1 ];
1260
- // Neither of the following are allowed.
1261
- const ALIAS_N_PLUS_1 : usize = N_PLUS_1 ;
1262
- let x : [u8 ; N_PLUS_1 ] = [0u8 ; ALIAS_N_PLUS_1 ];
1263
- const ALIAS_N : usize = <T >:: N ;
1264
- let x : [u8 ; <T >:: N ] = [0u8 ; ALIAS_N ];
1265
- ```
1205
+ If the value of an associated const depends on a type parameter (including
1206
+ ` Self ` ), it cannot be used in a constant expression. This restriction will
1207
+ almost certainly be lifted in the future, but this raises questions outside the
1208
+ scope of this RFC.
1266
1209
1267
1210
# Staging
1268
1211
@@ -1471,97 +1414,31 @@ on implementation concerns, which are not yet clear.
1471
1414
## Generic associated consts in match patterns
1472
1415
1473
1416
It seems desirable to allow constants that depend on type parameters in match
1474
- patterns, but it's not clear how to do so.
1475
-
1476
- Looking at the ` HasVar ` example above, one possibility would be to simply treat
1477
- the first, forbidden match expression as syntactic sugar for the second, allowed
1478
- match expression that uses a pattern guard. This is simple to implement because
1479
- one can simply ignore the constant when performing exhaustiveness and
1480
- reachability checks. Unfortunately, this approach blurs the difference between
1481
- match patterns (which provide strict checks) and pattern guards (which are just
1482
- useful syntactic sugar), and it does not increase the expressiveness of the
1483
- language.
1484
-
1485
- An alternative would be to allow ` where ` clauses to place constraints on
1486
- associated consts. If an associated const is known to be equal/unequal to some
1487
- other value (or in the case of integers, inside/outside a given range), this can
1488
- inform exhaustiveness and reachability checks. But this requires more design and
1489
- implementation work, and more syntax.
1417
+ patterns, but it's not clear how to do so while still checking exhaustiveness
1418
+ and reachability of the match arms. Most likely this requires new forms of
1419
+ where clause, to constrain associated constant values.
1490
1420
1491
1421
For now, we simply defer the question.
1492
1422
1493
1423
## Generic associated consts in array sizes
1494
1424
1495
- The above solution for type-checking array sizes is somewhat unsatisfactory. In
1496
- particular, it is counter-intuitive that neither of the following will type
1497
- check:
1425
+ It would be useful to be able to use trait-associated constants in generic code.
1498
1426
1499
1427
``` rust
1500
1428
// Shouldn't this be OK?
1501
1429
const ALIAS_N : usize = <T >:: N ;
1502
1430
let x : [u8 ; <T >:: N ] = [0u8 ; ALIAS_N ];
1503
- // This is likely to yield an embarrassing error message such as:
1504
- // "couldn't prove that `<T>::N + 1` is equal to `<T>::N + 1`"
1505
- let x : [u8 ; <T >:: N + 1 ] = [0u8 ; <T >:: N + 1 ];
1506
- ```
1507
-
1508
- A function like this is especially affected:
1509
-
1510
- ``` rust
1511
- trait HasN {
1512
- const N : usize ;
1513
- }
1514
- fn foo <T : HasN >() -> [u8 ; <T >:: N + 1 ] {
1515
- // Can't be verified to be correct for the return type, and can't use the
1516
- // intermediate const workaround due to scoping issues.
1517
- [0u8 ; <T >:: N + 1 ]
1518
- }
1431
+ // Or...
1432
+ let x : [u8 ; T :: N + 1 ] = [0u8 ; T :: N + 1 ];
1519
1433
```
1520
1434
1521
- This can be worked around with type-level naturals that use associated consts to
1522
- produce array sizes, but this is syntactically a bit inelegant.
1435
+ However, this causes some problems. What should we do with the following case in
1436
+ type checking, where we need to prove that a generic is valid for any ` T ` ?
1523
1437
1524
1438
``` rust
1525
- // Assume that `TypeAdd` and `One` are from a type-level naturals or similar
1526
- // library, and that `NAsTypeNatN` provides some way of translating the `N`
1527
- // on a `HasN` to a type compatible with that library.
1528
- trait HasN {
1529
- const N : usize ;
1530
- type TypeNatN ;
1531
- }
1532
- fn foo <T : HasN >() -> [u8 ; TypeAdd <<T >:: TypeNatN , One >:: AsUsize ] {
1533
- // Because the type `TypeAdd<<T>::TypeNatN, One>` can be verified to be
1534
- // equal to itself in type checking, we know that the associated const
1535
- // `AsUsize` below must be the same item as the `AsUsize` mentioned in the
1536
- // return type above.
1537
- [0u8 ; TypeAdd <<T >:: NAsTypeNat , One >:: AsUsize ]
1538
- }
1439
+ let x : [u8 ; T :: N + T :: N ] = [0u8 ; 2 * T :: N ];
1539
1440
```
1540
1441
1541
- There are a variety of possible ways to address the above issues, including:
1542
-
1543
- - Implementing smarter handling of consts that are just aliases of other
1544
- constant items.
1545
- - Allowing ` where ` clauses to constrain some associated constants to be equal,
1546
- to other expressions, and using this information in type checking.
1547
- - Adding normalization with little or no awareness of arithmetic (e.g. allowing
1548
- expressions that are exactly the same to be considered equal, or using only
1549
- a very basic understanding of which operations are commutative and/or
1550
- associative).
1551
- - Adding new syntax and/or new capability to plugins to allow type-level
1552
- naturals to be used with more ergonomic and clear syntax.
1553
- - Implementing a dependent type system that provides built-in semantics for
1554
- integer arithmetic at the type level, rather than implementing this in an
1555
- external or standard library.
1556
- - Using a full-fledged SMT solver.
1557
- - Some other creative solutions not on this list.
1558
-
1559
- While there are many ways to improve on the current design, and many of these
1560
- approaches are not mutually exclusive, much more work is needed to investigate
1561
- and implement a self-consistent, effective, and ideally intuitive set of
1562
- solutions.
1563
-
1564
- Though admittedly not very satisfying at the moment, the current approach has
1565
- the advantage of being (arguably) a good minimalist design, allowing associated
1566
- consts to be used for array sizes in generic code now, but also allowing for any
1567
- of a number of improved systems to be implemented later.
1442
+ We would like to handle at least some obvious cases (e.g. proving that
1443
+ ` T::N == T::N ` ), but without trying to prove arbitrary statements about
1444
+ arithmetic. The question of how to do this is deferred.
0 commit comments