この記事はRust 2 Advent Calendar 2020 22日目の記事です。
const genericsとは
const genericsとは下記のようなジェネリクスパラメーターに定数を渡せる機能です。
struct Foo<const N: usize> {}
let v = Foo::<5> {};
記事を書いている2020/12/22時点ではstableに入っていませんが、こちらのissueによるとRust 1.50でstableに入れることを予定しているそうです。
C++でテンプレートメタプログラミングをしていた方々には慣れ親しんだ機能だと思います。
const genericsを利用する方法
const genericsを利用するにはnightlyチャンネルで#![feature(min_const_generics)]
を書く必要があります
使用可能な箇所
基本的にジェネリクスを使用できる場所でならどこでも使用できます。
struct A<const N: i32>;
trait B<const N: i32> {}
fn function<const N: i32>() {}
enum E<const N: i32> {}
特定のパラメーターのものに対してtraitを実装することもできます。
struct A<const N: i32>;
trait Hoge {
fn hoge();
}
// Nが0のときのみHogeを実装
impl Hoge for A<0> {
fn hoge() {}
}
fn main() {
A::<0>::hoge(); // OK
A::<1>::hoge(); // コンパイルエラー
}
配列の長さに関わらず特定のtraitを実装することもできます。
trait Hoge {
fn hoge(&self) { }
}
impl<T, const N: usize> Hoge for [T; N] {
fn hoge(&self) { }
}
パラメーターの制限
定数の型は整数型もしくはbool
, char
だけが許可されます。
具体的にはu8
, u16
, u32
, u64
, u128
, usize
, i8
, i16
, i32
, i64
, i128
, isize
, char
, bool
だけが許可されます。
struct A<const N: f32>;
のようにf32
などを使用しようとするとコンパイルエラーが発生します。
error: `f32` is forbidden as the type of a const generic parameter
--> src/lib.rs:3:19
|
3 | struct A<const N: f32>;
| ^^^
|
= note: the only supported types are integers, `bool` and `char`
= help: more complex types are supported with `#[feature(const_generics)]`
計算結果やconst fn
の戻り値を渡すことができますが、{}で囲んでやる必要があります。
struct A<const N: i32>;
const fn function() -> i32 {
42
}
fn main() {
let _ = A::<{1 + 2}>{}; // OK
let _ = A::<1 + 2>{}; // コンパイルエラー
let _ = A::<{function()}>{}; // OK
let _ = A::<function()>{}; // コンパイルエラー
}
下記のように定数の型をジェネリクスで指定することはできません。
struct A<T, const N: T>;
近い将来に実装される可能性も低いそうです。
順番にも制限があり、定数は型パラメーターよりも後に来る必要があります。
struct A<T, const N: i32>; // 型が先なのでOK
struct B<const N: i32, T>; // 型が後なのでコンパイルエラー
あとがき
const genericsが使えるようになればジェネリックパラメーターに定数を渡せるようになります。
現状の予定では制限が多いですが便利な機能なのでstable入りが非常に楽しみです。
自分で使うことが少なくてもいろんなcrateがこの機能によって使いやすくなったり便利になったりするんじゃないかと思っています。
今回書いた内容は冒頭にもリンクを張ったこちらのissueを参考にしています。
興味がある方はissueの方も覗いてみてください。