4.6 KiB
What #[derive(Add)] generates
Note
:
Sub,BitAnd,BitOrandBitXorderives are fully equivalent to theAddderive described below.
Deriving Add works by adding two values structurally (field by field, according
to their type structure).
Structs
The derived Add implementation will allow two structs of the same type to be
added together. This is done by Adding their respective fields together and
creating a new struct with those values.
# use derive_more::Add;
#
#[derive(Add)]
struct MyInts(i32, i32);
#[derive(Add)]
struct Point2D {
x: i32,
y: i32,
}
This generates code equivalent to:
# use std::ops::Add;
#
# struct MyInts(i32, i32);
#
# struct Point2D {
# x: i32,
# y: i32,
# }
#
impl Add for MyInts {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Self(self_0, self_1), Self(rhs_0, rhs_1)) => {
Self(Add::add(self_0, rhs_0), Add::add(self_1, rhs_1))
}
}
}
}
impl Add for Point2D {
type Output = Self;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Self { x: self_0, y: self_1 }, Self { x: rhs_0, y: rhs_1 }) => {
Self { x: Add::add(self_0, rhs_0), y: Add::add(self_1, rhs_1) }
}
}
}
}
The behaviour is similar with more or less fields.
Ignoring
Sometimes a struct needs to hold a field (most commonly PhantomData) that doesn't
participate in Add implementation. Such field could be ignored using the #[add(skip)]
attribute.
# use core::marker::PhantomData;
# use derive_more::Add;
#
#[derive(Add)]
struct TupleWithZst<T>(i32, #[add(skip)] PhantomData<T>);
#[derive(Add)]
struct StructWithZst<T> {
x: i32,
#[add(skip)] // or #[add(ignore)]
_marker: PhantomData<T>,
}
Enums
For enums each variant can be Added in a similar way to another instance of the
same variant. There's one big difference however: it returns a Result<EnumType>,
because an error is returned when two different variants are Added together.
# use derive_more::Add;
#
#[derive(Add)]
enum MixedInts {
SmallInt(i32),
BigInt(i64),
TwoSmallInts(i32, i32),
NamedSmallInts { x: i32, y: i32 },
UnsignedOne(u32),
UnsignedTwo(u32),
Unit,
}
This generates code equivalent to:
# use std::ops::Add;
#
# enum MixedInts {
# SmallInt(i32),
# BigInt(i64),
# TwoSmallInts(i32, i32),
# NamedSmallInts { x: i32, y: i32 },
# UnsignedOne(u32),
# UnsignedTwo(u32),
# Unit,
# }
#
impl Add for MixedInts {
type Output = Result<Self, derive_more::BinaryError>;
fn add(self, rhs: Self) -> Self::Output {
match (self, rhs) {
(Self::SmallInt(self_0), Self::SmallInt(rhs_0)) => {
Ok(Self::SmallInt(Add::add(self_0, rhs_0)))
}
(Self::BigInt(self_0), Self::BigInt(rhs_0)) => {
Ok(Self::BigInt(Add::add(self_0, rhs_0)))
}
(Self::TwoSmallInts(self_0, self_1), Self::TwoSmallInts(rhs_0, rhs_1)) => {
Ok(Self::TwoSmallInts(Add::add(self_0, rhs_0), Add::add(self_1, rhs_1)))
}
(Self::NamedSmallInts { x: self_0, y: self_1 },
Self::NamedSmallInts { x: rhs_0, y: rhs_1 }) => {
Ok(Self::NamedSmallInts {
x: Add::add(self_0, rhs_0),
y: Add::add(self_1, rhs_1),
})
}
(Self::UnsignedOne(self_0), Self::UnsignedOne(rhs_0)) => {
Ok(Self::UnsignedOne(Add::add(self_0, rhs_0)))
}
(Self::UnsignedTwo(self_0), Self::UnsignedTwo(rhs_0)) => {
Ok(Self::UnsignedTwo(Add::add(self_0, rhs_0)))
}
(Self::Unit, Self::Unit) => Err(derive_more::BinaryError::Unit(
derive_more::UnitError::new("add"),
)),
_ => Err(derive_more::BinaryError::Mismatch(
derive_more::WrongVariantError::new("add"),
)),
}
}
}
Also note the Unit variant that throws a derive_more::UnitError when Adding it to itself.
Ignoring
Similarly to structs, enum fields could be ignored using the #[add(skip)] attribute.
# use derive_more::Add;
#
struct I32(i32); // doesn't implement `Add`
#[derive(Add)]
enum MixedInts {
TwoSmallInts(i32, #[add(skip)] I32),
NamedSmallInts {
#[add(skip)] // or #[add(ignore)]
x: I32,
y: i32,
},
}
Note
: Ignoring all the fields of a variant or ignoring the variant itself is not allowed (results in a compilation error).