Summary
Implement generics over integers, similar to integer templates in C++ (template<int n>
). The point of this RFC is to just implement the bare essentials underneath a feature flag, and then flesh out the design with further RFCs.
Motivation
- Algebraic Types: Matrices, Vectors, would all benefit from compile time integer generics. Matrix<2, 2> + Matrix<3, 3> doesn’t make any sense, and it’d be great to have a compile time error for that.
- Compile Time Sizes: Like the StackVec example below; we have examples, mostly using typenum, but that’s a hack of the typesystem. It would be really nice to be able to write these without resorting to these kinds of crates.
- Arrays: Our arrays are kind of terrible, this would make them slightly better.
impl
ing Clone for all arrays whereT: Clone
will fix an annoying ICE as well. - Physical Units: Saying that a kilometer is, at compile time, 1000 meters is a nice bonus.
Detailed Design
Basic Syntax
This utilizes the syntax of generics. It’s fairly simple.
fn adder<const n: u32>(m: u32) -> u32 {
n + m
}
// There are better ways to implement this, but it's an example
struct StackVec<T, const n: usize> where T: Default{
store: [T; n],
len: usize,
}
impl<T, const n: usize> StackVec<T, const n> where T: Default {
pub fn new() -> StackVec<T, n> {
StackVec {
store: [Default::default(); n],
len: 0,
}
}
}
which will be called as
println!("{}", adder::<const 4>(5)) // '9'
// or
let v = StackVec::<_, const 16>::new();
however, if you have a type
struct Wrapper<const n: usize>(pub [u8; n]);
then Rust will be able to unify the following
let w = Wrapper([u8; 15]);
to be a Wrapper<const 15>
.
Ordering
The ordering of generic arguments shall be lifetime, type, constants.
struct BorrowedArray<'a, T, const n: usize>(&'a [T; n]);
Allowed Types
At first, we shall allow integer types of any size. This will likely be implemented in the compiler as a u64
for any constant. However, if there is demand later, we will be able to expand this system to full dependent types. This RFC does not cover that scope.
Where Clauses
Where clauses are not implemented by this RFC. If they are felt necessary, they can be added later, backwards compatibly.
Drawbacks
More complexity
Alternatives
We see alternatives like typenum. These are ingenious, but an unfortunate and ugly outcropping of us not having a necessary feature.
Unresolved Questions
- Should we allow integers of any size? Or perhaps we should do like C++ does, and only have usize.
- Should we only write const once per generic list? It gets annoying after a while to keep writing the same word over, and over.
impl<const n, const m> std::ops::Add<Matrix<const n, const m>>
for Matrix<const n, const m> {
type Output = Matrix<const n, const m>;
}
// versus
impl<const n, m> std::ops::Add<Matrix<const n, m>> for Matrix<const n, m> {
type Output = Matrix<const n, m>;
}