В формулировке темы мог допустить ошибку, однако:
мне нужна была “универсальная единица измерения” для разных координатных сеток и я сделал:
pub struct Unit<P, V>(pub(crate) [V; 9], PhantomData<P>);
здесь P
- это система координат,
V
- значения координат,
9 - максимальное число осей в конкретно моем случае
добавил системы координат:
1.
pub enum Axis {
X = 0,
Y,
Z,
U,
V,
W,
A,
B,
C,
}
pub enum Driver {
D0 = 0,
D1,
D2,
D3,
D4,
D5,
D6,
D7,
}
для доступа к значениям единицы измерения реализовал типаж Index
и IndexMut
impl<V> Index<Axis> for Unit<Axis, V> {
type Output = V;
fn index(&self, index: Axis) -> &Self::Output {
&self.0[index as usize]
}
}
impl<V> IndexMut<Axis> for Unit<Axis, V> {
fn index_mut(&mut self, index: Axis) -> &mut Self::Output {
&mut self.0[index as usize]
}
}
impl<V> Index<Driver> for Unit<Driver, V> {
type Output = V;
fn index(&self, index: Driver) -> &Self::Output {
&self.0[index as usize]
}
}
impl<V> IndexMut<Driver> for Unit<Driver, V> {
fn index_mut(&mut self, index: Driver) -> &mut Self::Output {
&mut self.0[index as usize]
}
}
а дальше, мне понадобилось реализовать однотипные функции для Unit<Axis, V>
и Unit<Driver, V>
c “общим телом”, и оказалось что это можно сделать так:
impl<P, V> Unit<P, V>
where
Unit<P, V>: Index<P, Output = V>, // ого, можно ограничить собственный тип!
{
pub fn get(&self, index: P) -> &V { // простая функция приведена в качестве примера
self.index(index)
}
}
дальше возникла потребность в более сложном типе:
pub enum Path<P, V> {
Line(Unit<P, V>),
Arc {
direction: Direction,
distance: Unit<P, V>,
radius: Unit<P, V>,
offset: Unit<P, V>,
},
}
и здесь мне тоже было нужно что бы P
выступал в качестве индекса. Оказалось что это можно сделать так:
impl<P> Path<P, f32>
where
Unit<P, f32>: Index<P, Output = f32>, // вот это вообще для меня фантастика, я не знал что так можно
P: Copy,
{
pub fn plane(&self, index: P) -> f32 {
match self {
Path::Line(distance) => *distance.get(index),
Path::Arc {
direction: _,
distance,
..
} => *distance.get(index),
}
}
}
У меня прям здорово уменьшилась кодовая база за счет того,
что раньше каждую функцию для Unit<P, V>
и Path<P, V>
приходилось реализовывать и для Axis
и для Driver
, а сейчас я смог обобщить часть функций.
Я не видел ни чего подобного ни в книге ни в стандартной библиотеки.
Интересно, а можно ли как-то указать, в реализации, что конкретное поле объекта должно реализовать конкретный типаж? Ну что-то вроде такого:
impl<P> Path<P, f32>
where
Path<P, f32>::Arc.radius: Index<P, Output = f32> {
...
}