const fnで使用可能なVec3を定義する
2022/01/27 13:25 | 公開 |
この記事はRust v1.60.0-nightlyで動作確認しています。
今私はRustでコンパイル時レイトレーシングという試みをしているのですが、そこで必要になったconst fn
の中で使用できるVec3
を定義するためのに必要なことを書いていきます。
都度Rust Playgroundへのリンクを張っていますが、コンパイルに時間がかかることがありRust Playgroundではタイムアウトするかもしれません。
struct Vec3 {
x: f64,
y: f64,
z: f64,
}
このようなVec3
があるとしてconst fn
の中で
const fn const_function() {
let mut v1: Vec3 = Vec3 { x: 1.0, y: 2.0, z: 3.0 };
let v2: Vec3 = Vec3 { x: 2.0, y: 3.0, z: 4.0 };
let _ = v1 + v2; // Add
v += Vec3 { x: 2.0, y: 3.0, z: 4.0 }; // AddAssign
v[0] = 0.0; // IndexMut
}
上記のようなことができるようにします。
Add
まずconst fn
の中で動くAdd
を定義してみます。
下記のように通常のAddを定義してconst fn
の中で使ってみたものをコンパイルしてみると
use std::ops::*;
struct Vec3 {
x: f64,
y: f64,
z: f64,
}
impl Add<Vec3> for Vec3 {
type Output = Self;
fn add(self, rhs: Vec3) -> Self::Output {
Vec3 {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
const fn const_function() {
let v1: Vec3 = Vec3 { x: 1.0, y: 2.0, z: 3.0 };
let v2: Vec3 = Vec3 { x: 2.0, y: 3.0, z: 4.0 };
let _ = v1 + v2; // Add
}
Compiling playground v0.0.1 (/playground)
error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
--> src/lib.rs:25:11
|
25 | let _ = v1 + v2; // Add
| ^^^^^^^
For more information about this error, try `rustc --explain E0015`.
error: could not compile `playground` due to previous error
のようなコンパイルエラーが発生します。
Rust Playgroundで確認
まずAdd
Traitの実装をconst fn
としてできるように#![feature(const_trait_impl)]
を追記し、
impl Add<Vec3> for Vec3
をimpl const Add<Vec3> for Vec3
に書き換えます。
コード全文
#![feature(const_trait_impl)]
use std::ops::*;
struct Vec3 {
x: f64,
y: f64,
z: f64,
}
impl const Add<Vec3> for Vec3 {
type Output = Self;
fn add(self, rhs: Vec3) -> Self::Output {
Vec3 {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
const fn const_function() {
let v1: Vec3 = Vec3 { x: 1.0, y: 2.0, z: 3.0 };
let v2: Vec3 = Vec3 { x: 2.0, y: 3.0, z: 4.0 };
let _ = v1 + v2; // Add
}
すると今度はこのようなエラーが出ます。
Compiling playground v0.0.1 (/playground)
error[E0658]: floating point arithmetic is not allowed in constant functions
--> src/lib.rs:15:16
|
15 | x: self.x + rhs.x,
| ^^^^^^^^^^^^^^
|
= note: see issue #57241 <https://github.com/rust-lang/rust/issues/57241> for more information
= help: add `#![feature(const_fn_floating_point_arithmetic)]` to the crate attributes to enable
error[E0658]: floating point arithmetic is not allowed in constant functions
--> src/lib.rs:16:16
|
16 | y: self.y + rhs.y,
| ^^^^^^^^^^^^^^
|
= note: see issue #57241 <https://github.com/rust-lang/rust/issues/57241> for more information
= help: add `#![feature(const_fn_floating_point_arithmetic)]` to the crate attributes to enable
error[E0658]: floating point arithmetic is not allowed in constant functions
--> src/lib.rs:17:16
|
17 | z: self.z + rhs.z,
| ^^^^^^^^^^^^^^
|
= note: see issue #57241 <https://github.com/rust-lang/rust/issues/57241> for more information
= help: add `#![feature(const_fn_floating_point_arithmetic)]` to the crate attributes to enable
For more information about this error, try `rustc --explain E0658`.
error: could not compile `playground` due to 3 previous errors
これはconst fn
の中で浮動小数点演算が行えないというエラーなので#![feature(const_fn_floating_point_arithmetic)]
を書くことで解決できます。
変更後のコードは差分が少ないので載せませんがRust Playgroundで確認できるので気になる方はリンク先を確認してみてください。
AddAssign
次にAddAssign
を定義します。
impl const AddAssign<Vec3> for Vec3 {
fn add_assign(&mut self, rhs: Vec3) {
*self = Vec3 {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
const fn const_function() {
let mut v1: Vec3 = Vec3 { x: 1.0, y: 2.0, z: 3.0 };
let v2: Vec3 = Vec3 { x: 2.0, y: 3.0, z: 4.0 };
v1 += v2;
}
上記のようにAdd
と同じようにimpl const AddAssign
で定義してconst fn
で使用してみると
Compiling playground v0.0.1 (/playground)
error[E0658]: mutable references are not allowed in constant functions
--> src/lib.rs:25:19
|
25 | fn add_assign(&mut self, rhs: Vec3) {
| ^^^^^^^^^
|
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
error[E0658]: mutable references are not allowed in constant functions
--> src/lib.rs:38:3
|
38 | v1 += v2;
| ^^
|
= note: see issue #57349 <https://github.com/rust-lang/rust/issues/57349> for more information
= help: add `#![feature(const_mut_refs)]` to the crate attributes to enable
For more information about this error, try `rustc --explain E0658`.
error: could not compile `playground` due to 2 previous errors
というエラーが出ます。
Rust Playgroundで確認
これは&mut
な変数を引数にする関数がconst fn
の中で使えないという内容で#![feature(const_mut_refs)]
を追加してやることで解決できます。
コード全文
#![feature(const_trait_impl)]
#![feature(const_fn_floating_point_arithmetic)]
#![feature(const_mut_refs)]
use std::ops::*;
struct Vec3 {
x: f64,
y: f64,
z: f64,
}
impl const Add<Vec3> for Vec3 {
type Output = Self;
fn add(self, rhs: Vec3) -> Self::Output {
Vec3 {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
impl const AddAssign<Vec3> for Vec3 {
fn add_assign(&mut self, rhs: Vec3) {
*self = Vec3 {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
const fn const_function() {
let mut v1: Vec3 = Vec3 { x: 1.0, y: 2.0, z: 3.0 };
let v2: Vec3 = Vec3 { x: 2.0, y: 3.0, z: 4.0 };
v1 += v2;
}
IndexMut
続いてIndexMut
を定義します。
IndexMut
の定義はIndex
の定義が必要なので両方定義します。
impl const Index<usize> for Vec3 {
type Output = f64;
fn index(&self, i: usize) -> &Self::Output {
match i {
0 => &self.x,
1 => &self.y,
2 => &self.z,
_ => panic!("out of range."),
}
}
}
impl const IndexMut<usize> for Vec3 {
fn index_mut(&mut self, i: usize) -> &mut Self::Output {
match i {
0 => &mut self.x,
1 => &mut self.y,
2 => &mut self.z,
_ => panic!("out of range."),
}
}
}
ここまでに
#![feature(const_trait_impl)]
, #![feature(const_fn_floating_point_arithmetic)]
, #![feature(const_mut_refs)]
を有効にしているため、特別なことをする必要はありません。
index
とindex_mut
の中でpanic!
を使用していますが、これは何もせずともconst fn
の中で使用できます。
これで
const fn const_function() {
let mut v: Vec3 = Vec3 { x: 1.0, y: 2.0, z: 3.0 };
v[0] = 0.0;
}
という処理のコンパイルが通るようになりました。
コード全文
#![feature(const_trait_impl)]
#![feature(const_fn_floating_point_arithmetic)]
#![feature(const_mut_refs)]
use std::ops::*;
struct Vec3 {
x: f64,
y: f64,
z: f64,
}
impl const Add<Vec3> for Vec3 {
type Output = Self;
fn add(self, rhs: Vec3) -> Self::Output {
Vec3 {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
impl const AddAssign<Vec3> for Vec3 {
fn add_assign(&mut self, rhs: Vec3) {
*self = Vec3 {
x: self.x + rhs.x,
y: self.y + rhs.y,
z: self.z + rhs.z,
}
}
}
impl const Index<usize> for Vec3 {
type Output = f64;
fn index(&self, i: usize) -> &Self::Output {
match i {
0 => &self.x,
1 => &self.y,
2 => &self.z,
_ => panic!("out of range."),
}
}
}
impl const IndexMut<usize> for Vec3 {
fn index_mut(&mut self, i: usize) -> &mut Self::Output {
match i {
0 => &mut self.x,
1 => &mut self.y,
2 => &mut self.z,
_ => panic!("out of range."),
}
}
}
const fn const_function() {
let mut v: Vec3 = Vec3 { x: 1.0, y: 2.0, z: 3.0 };
v[0] = 0.0;
}
Rust Playgroundで確認
コンパイルに時間がかかることがありRust Playgroundではタイムアウトするかもしれません。
panic!内での変数埋め込みはできない
panic!
が使えるならpanic!("out of range. index = {i}")
のようにどの値が渡されたことで範囲外アクセスが発生したのかをコンパイルエラーのメッセージに表示したくなると思います。
しかし普通に書いても下記のようなコンパイルエラーが発生してしまいます。
Compiling playground v0.0.1 (/playground)
error[E0658]: function pointer casts are not allowed in constant functions
--> src/lib.rs:43:48
|
43 | _ => panic!("out of range. index = {i}"),
| ^^^
|
= note: see issue #57563 <https://github.com/rust-lang/rust/issues/57563> for more information
= help: add `#![feature(const_fn_fn_ptr_basics)]` to the crate attributes to enable
= note: this error originates in the macro `$crate::const_format_args` (in Nightly builds, run with -Z macro-backtrace for more info)
error[E0015]: calls in constant functions are limited to constant functions, tuple structs and tuple variants
--> src/lib.rs:43:18
|
43 | _ => panic!("out of range. index = {i}"),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: this error originates in the macro `$crate::const_format_args` (in Nightly builds, run with -Z macro-backtrace for more info)
Some errors have detailed explanations: E0015, E0658.
For more information about an error, try `rustc --explain E0015`.
error: could not compile `playground` due to 2 previous errors
これはコンパイルエラーにあるように#![feature(const_fn_fn_ptr_basics)]
を追記してもコンパイルは通りません。
ビルド時の引数次第でなんとかなりそうなエラーは出ていましたがそこまで確認はしていないです。
まとめ
#![feature(const_trait_impl)]
, #![feature(const_fn_floating_point_arithmetic)]
, #![feature(const_mut_refs)]
を追加しTraitの実装時にimpl const
と書いてやれば普通に定義するのと変わらないように書くことができます。