I'm currently implementing a ray tracer following along the book "The Ray Tracer Challenge" by Jamis Buck.
I've arrived at the part where I have to implement a few methods on matrices, and since these matrices have a compile time known size, I chose to implement them using const generics expressions
(which are still only available on the nightly channel).
#![feature(generic_const_exprs)]
The Matrix
type is defined as the following:
#[derive(Debug)]
pub struct Matrix<const N: usize>(pub [[f64; N]; N]);
I implemented the following methods:
impl<const N: usize> Matrix<N> {
fn submatrix(&self, index: Idx) -> Matrix<{ N - 1 }> {
//...
}
fn minor(&self, index: Idx) -> f64
where
[(); N - 1]:,
{
let submatrix = self.submatrix(index);
submatrix.determinant()
}
fn cofactor(&self, index: Idx) -> f64
where
[(); N - 1]:,
{
let minor = self.minor(index);
//...
}
fn determinant(&self) -> f64
where
[(); N - 1]:,
{
//...
}
}
... but I'm running into an issue with the submatrix
method, which returns a Matrix<{ N - 1 }>
.
When calling the determinant
method on a submatrix
, which is what we do in the minor
method:
fn minor(&self, index: Idx) -> f64
where
[(); N - 1]:,
{
let submatrix: Matrix<{ N - 1 }> = self.submatrix(index);
submatrix.determinant()
}
... even though the minor
method is bounded by the following constraint:
where [(); N - 1]:,
... the compiler still complains and suggests to add the same where bound using this expression
:
error: unconstrained generic constant
--> src/rt/matrix.rs:109:19
|
109 | submatrix.determinant()
| ^^^^^^^^^^^
|
= help: try adding a `where` bound using this expression: `where [(); N - 1]:`
note: required by a bound in `Matrix::<N>::determinant`
--> src/rt/matrix.rs:128:14
|
126 | fn determinant(&self) -> f64
| ----------- required by a bound in this
127 | where
128 | [(); N - 1]:,
| ^^^^^ required by this bound in `Matrix::<N>::determinant`
I tried to implement the minor
method for Matrix<{ N - 1 }>
but it does not seem to work either (or rather I have no idea how to do that, or if that's even possible):
impl<const N: usize> Matrix<{ N - 1 }> {
fn minor(&self, index: Idx) -> f64 {
let submatrix = self.submatrix(index);
submatrix.determinant()
}
... which gives the following error:
error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates
--> src/rt/matrix.rs:138:12
|
138 | impl<const N: usize> Matrix<{ N - 1 }> {
| ^ unconstrained const parameter
|
= note: expressions using a const parameter must map each value to a distinct output value
= note: proving the result of expressions other than the parameter are unique is not supported
I'm wondering if what I'm trying to do here with the submatrix
is even possible. It's not a big deal since I can define these methods for each possible N, which in my case are limited to 2_usize
, 3_usize
and 4_usize
, but implementing these methods only for Matrix<N>
would be a lot cleaner!
Warning: The generic_const_exprs
feature is extremely unstable! Do not use it in production!
You call determinant()
on submatrix
, which is already Matrix<{ N - 1 }>
(returned from submatrix()
). So you can to also restrict it to where [(); N - 1 - 1]:,
(note that the Rust compiler is not smart enough to understand this is the same as where [(); N - 2]:,
, nor it is able to conclude that if this holds then also where [(); N - 1]:,
. You have to write both: where [(); N - 1]:, [(); N - 1 - 1]:,
):
fn minor(&self, index: Idx) -> f64
where
[(); N - 1]:,
[(); N - 1 - 1]:,
{
let submatrix = self.submatrix(index);
submatrix.determinant()
}
fn cofactor(&self, index: Idx) -> f64
where
[(); N - 1]:,
[(); N - 1 - 1]:,
{
let minor = self.minor(index);
//...
}