如何通过副作用实现变量增量?

How to implement variable increment via side effect?

出于学习目的,我尝试了这个解决方案,但是 it does not work:

use std::ops::Add;

fn inc<T: Add>(x:&mut T) {
    *x += 1; 
}

fn main() {
    let mut x:i32 = 10;
    let mut y:u8 = 1;
    inc(&mut x);
    inc(&mut y);
    println!("{} {}", x, y);
}

错误信息:

<anon>:4:5: 4:7 error: binary assignment operation `+=` cannot be applied to types `T` and `_` [E0368]
<anon>:4     *x += 1; 
             ^~
<anon>:4:5: 4:7 help: see the detailed explanation for E0368
error: aborting due to previous error

正确的做法是什么?

目前,+=仅在原始整数类型上定义;通常,您需要将其扩展为 *x = *x + 1;。这会揭示更多问题:

<anon>:4:15: 4:16 error: mismatched types:
 expected `T`,
    found `_`
(expected type parameter,
    found integral variable) [E0308]
<anon>:4     *x = *x + 1; 
                       ^
<anon>:4:10: 4:16 error: mismatched types:
 expected `T`,
    found `<T as core::ops::Add>::Output`
(expected type parameter,
    found associated type) [E0308]
<anon>:4     *x = *x + 1; 
                  ^~~~~~
error: aborting due to 2 previous errors

让我们看看Add特征的定义:

pub trait Add<RHS = Self> {
    /// The resulting type after applying the `+` operator
    type Output;

    /// The method for the `+` operator
    fn add(self, rhs: RHS) -> Self::Output;
}

因此 Self + RHS 生成一个类型为 <Self as Add<RHS>>::Output 的对象。

当您将值存储回 *x 时,计算结果必须是 T;因此我们确定 T 上的界限需要不是 Add 而是 Add<<em>???</em>, Output = T>.

那么,<em>会是什么???</em>会是什么? 1 的类型是什么?这不是通用的;它是十种已知原始整数类型之一(isizei8i16i32i64usizeu8, u16, u32, u64)。这显然行不通,因为整数类型不实现较小类型的加法——RHS 的默认值 Self(即,T: Add 表示 T: Add<Self>) 是您可以指望的全部,但是 1 不能是 T.

类型

解决方案是使用生成值 1 的通用函数。 std::num::One 中有一个不稳定的,crates.io、num::Onenum 箱子中有一个稳定的。使用前者需要 Rust nightly,使用后者需要删除 std::,添加 extern crate num; 并将 num 添加到 Cargo.toml 依赖项部分。

我们还需要一个 Copy 绑定来允许 *x + 1*x 工作。

Here’s the final result:

#![feature(zero_one)]

use std::ops::Add;
use std::num::One;

fn inc<T: Copy + One + Add<T, Output = T>>(x: &mut T) {
    *x = *x + T::one(); 
}

fn main() {
    let mut x: i32 = 10;
    let mut y: u8 = 1;
    inc(&mut x);
    inc(&mut y);
    println!("{} {}", x, y);
}