Rustコトハジメ

プログラミング言語Rustと競プロに関する情報をお届けします。

整数の参照が足し算出来る理由

このコードって驚きますか?C言語出身の人からすると、何を言ってるんだという感じになると思いますが、

fn main() {
    let a = 5;
    let b = 3;
    println!("{},{},{},{}", a+b, a+&b, &a+b, &a+&b)
}

結果は、

8,8,8,8

となります。

この背景にあるのはマクロです。整数への算術演算は、マクロによって生成されています。Rustのライブラリコードではこういう感じのマクロが多用されています。

macro_rules! add_impl {
    ($($t:ty)*) => ($(
        #[stable(feature = "rust1", since = "1.0.0")]
        impl Add for $t {
            type Output = $t;

            #[inline]
            #[rustc_inherit_overflow_checks]
            fn add(self, other: $t) -> $t { self + other }
        }

        forward_ref_binop! { impl Add, add for $t, $t }
    )*)
}
add_impl! { usize u8 u16 u32 u64 u128 isize i8 i16 i32 i64 i128 f32 f64 }

// implements binary operators "&T op U", "T op &U", "&T op &U"
// based on "T op U" where T and U are expected to be `Copy`able
macro_rules! forward_ref_binop {
    (impl $imp:ident, $method:ident for $t:ty, $u:ty) => {
        forward_ref_binop!(impl $imp, $method for $t, $u,
                #[stable(feature = "rust1", since = "1.0.0")]);
    };
    (impl $imp:ident, $method:ident for $t:ty, $u:ty, #[$attr:meta]) => {
        #[$attr]
        impl<'a> $imp<$u> for &'a $t {
            type Output = <$t as $imp<$u>>::Output;

            #[inline]
            fn $method(self, other: $u) -> <$t as $imp<$u>>::Output {
                $imp::$method(*self, other)
            }
        }
        ....

書いてあるとおり、T + Uをベースにして、&T + UT + &U&T + &Uを生成しています。

ではこれがどんなところで役立つか?ということですが、

fn main() {
    let v1 = vec![1,2,3];
    let v2: Vec<i32> = v1.iter().map(|x| x + 1).collect();
    println!("{:?}", v2);
}
[2, 3, 4]

のように書けるようになります。

クロージャの引数のxの型は&i32なのですが、&i32 + i32の足し算が実装されているため、こう書くことが出来るのです。