Ответ 1
Есть ли способ сделать это без извлечения их "внутренних" значений каждый раз с сопоставлением с образцом и без реализации черт Add, Sub,... и операторов перегрузки?
Нет, единственный способ - реализовать черты вручную. Rust не имеет эквивалента расширения Haskell GHC GeneralizedNewtypeDeriving
, которое позволяет deriving
на типах-обёртках автоматически реализовывать любой тип/черту типа, который реализует обернутый тип (и с текущей настройкой Rust #[derive]
как простое преобразование AST, реализовать его наподобие Haskell практически невозможно.)
Чтобы сократить процесс, вы можете использовать макрос:
use std::ops::{Add, Sub};
macro_rules! obvious_impl {
(impl $trait_: ident for $type_: ident { fn $method: ident }) => {
impl $trait_<$type_> for $type_ {
type Output = $type_;
fn $method(self, $type_(b): $type_) -> $type_ {
let $type_(a) = self;
$type_(a.$method(&b))
}
}
}
}
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug)]
pub struct Centimeters(i32);
obvious_impl! { impl Add for Centimeters { fn add } }
obvious_impl! { impl Sub for Centimeters { fn sub } }
#[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Debug)]
pub struct Inches(i32);
obvious_impl! { impl Add for Inches { fn add } }
obvious_impl! { impl Sub for Inches { fn sub } }
fn main() {
let a = Centimeters(100);
let b = Centimeters(200);
let c = Inches(10);
let d = Inches(20);
println!("{:?} {:?}", a + b, c + d); // Centimeters(300) Inches(30)
// error:
// a + c;
}
Я эмулировал нормальный синтаксис impl
в макросе, чтобы было понятно, что происходит, просто глядя на вызов макроса (т.е. уменьшая необходимость смотреть на определение макроса), а также чтобы поддерживать естественную возможность поиска в Rust: если вы ища черты в Centimeters
просто grep для for Centimeters
, и вы найдете эти макро-вызовы вместе с обычными impl
s.
Если вы часто обращаетесь к содержимому типа Centimeters
, вы можете рассмотреть возможность использования правильной структуры с полем для определения оболочки:
struct Centimeters { amt: i32 }
Это позволяет вам писать self.amt
вместо того, чтобы выполнять сопоставление с образцом. Вы также можете определить функцию, подобную fn cm(x: i32) -> Centimeters { Centimeters { amt: x } }
, называемую как cm(100)
, чтобы избежать многословия построения полной структуры.
Вы также можете получить доступ к внутренним значениям структуры кортежа, используя синтаксис .0
, .1
.