在 Rust 中实现通用的可递增特征
Implementing a generic incrementable trait in Rust
我正在尝试了解如何在 Rust 中实现通用特征。
虽然我看过很多示例,但这些示例过于依赖特定用途(例如 ),因此我无法在 Rust 开发的这一点上理解。
相反,这是一个基于相当普遍的东西的简单示例——递增:
trait Incrementable {
fn post_inc(&mut self) -> Self;
fn post_inc_by(&mut self, n: usize) -> Self;
}
impl Incrementable for usize {
fn post_inc(&mut self) -> Self {
let tmp = *self;
*self += 1;
tmp
}
//"Overload" for full generalizability
fn post_inc_by(&mut self, n: usize) -> Self {
let tmp = *self;
*self += n;
tmp
}
}
fn main() {
let mut result = 0;
assert!(result.post_inc() == 0);
assert!(result == 1);
assert!(result.post_inc_by(3) == 1);
assert!(result == 4);
}
以上代码有效,但存在不足,因为它无法在不编写大量样板代码的情况下推广到所有数字类型。
在我尝试概括上述代码的过程中,我与类型系统发生了争执,借用了检查器,或者被迫走上了实现 FromPrimitive
我想在我的泛型中支持的每种类型的道路版本(实际上让我回到原点)。
你能帮我理解如何实现 Incrementable
吗?
这样 post_inc()
和 post_inc_by()
至少适用于所有整数和浮点类型,理想情况下不必为每种类型编写实现?
我希望答案能帮助我了解特征、实现、类型和关联类型如何在比我遇到的更直接的用例中协同工作。
我正在使用 Rust 1.16.0。
我们可以递增的类型需要
- 知道运营商和
+=
(AddAssign
)
- 为 "one"-元素定义一个值
- 可复制,因为我们想保留旧的 un-incremented 值。
第 1 点和第 3 点,我们可以通过使用特征边界来确保,对于第 2 点,我们可以设置一个具有函数 one() -> self
.
的特征
所以这是一个工作示例:
// We need to know the operator "+="
use std::ops::AddAssign;
// The trait needs a type parameter
trait Incrementable<T> {
fn post_inc(&mut self) -> Self;
fn post_inc_by(&mut self, n: T) -> Self;
}
// We need a trait which tells us the "one" value for a type
trait Increment {
fn one() -> Self;
}
// We need to implement the Increment trait for every type
// we want to increment.
impl Increment for usize {
fn one() -> usize {
1
}
}
// Finally we implement the Increment trait generically for all types that
// * know the operator "+=" AddAssign
// * are copyable
// * implement our Increment trait, so that we know their "one" value
impl<T: AddAssign + Increment + Copy> Incrementable<T> for T {
fn post_inc(&mut self) -> Self {
let tmp = *self;
*self += T::one();
tmp
}
//"Overload" for full generalizability
fn post_inc_by(&mut self, n: T) -> Self {
let tmp = *self;
*self += n;
tmp
}
}
fn main() {
let mut result = 0;
assert!(result.post_inc() == 0);
assert!(result == 1);
assert!(result.post_inc_by(3) == 1);
assert!(result == 4);
}
您不必为每种类型编写 Incrementable
的实现,但您必须实现提供 one()
函数的特性。没有它就无法逃脱,因为对于非数字类型,"increment by one" 的含义并不明显。
我将所有内容都保留在可以通用实现的通用实现中。例外是 T::one()
,因此除了每个类型的一个简单函数外,不需要 boiler-plate 代码。
您可以使用宏执行此操作,遵循 what the std did:
trait Incrementable {
fn post_inc(&mut self) -> Self;
fn post_inc_by(&mut self, n: Self) -> Self;
}
macro_rules! post_inc_impl {
($($t:ty)*) => ($(
impl Incrementable for $t {
fn post_inc(&mut self) -> Self {
self.post_inc_by(1 as Self)
}
fn post_inc_by(&mut self, n: Self) -> Self {
let tmp = *self;
*self += n;
tmp
}
}
)*)
}
post_inc_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
fn main() {
let mut result = 0;
assert!(result.post_inc() == 0);
assert!(result == 1);
assert!(result.post_inc_by(3) == 1);
assert!(result == 4);
}
如果您使用 num
crate:
可以不用宏
extern crate num;
use num::Num;
trait Incrementable<T: Num> {
fn post_inc(&mut self) -> Self;
fn post_inc_by(&mut self, n: T) -> Self;
}
impl<T: Num + std::ops::AddAssign<T> + Copy> Incrementable<T> for T {
fn post_inc(&mut self) -> T {
let tmp = *self;
*self += T::one();
tmp
}
fn post_inc_by(&mut self, n: T) -> Self {
let tmp = *self;
*self += n;
tmp
}
}
@Simon Whitehead's example 可以很容易地适应稳定的 Rust:
trait Incrementable: Copy + std::ops::AddAssign<Self> {
fn one() -> Self;
fn post_inc(&mut self) -> Self {
self.post_inc_by(Self::one())
}
fn post_inc_by(&mut self, n: Self) -> Self {
let tmp = *self;
*self += n;
tmp
}
}
impl Incrementable for u8 { fn one() -> Self {1} }
impl Incrementable for u16 { fn one() -> Self {1} }
impl Incrementable for u32 { fn one() -> Self {1} }
impl Incrementable for u64 { fn one() -> Self {1} }
impl Incrementable for i8 { fn one() -> Self {1} }
impl Incrementable for i16 { fn one() -> Self {1} }
impl Incrementable for i32 { fn one() -> Self {1} }
impl Incrementable for i64 { fn one() -> Self {1} }
impl Incrementable for f32 { fn one() -> Self {1.0} }
impl Incrementable for f64 { fn one() -> Self {1.0} }
虽然您需要为每种类型进行实现,但每种类型都非常简单。
您还可以使用宏来隐藏重复的实现:
macro_rules! impl_Incrementable{
($($m:ty),*) => {$( impl Incrementable for $m { fn one() -> Self { 1 as $m } })*}
}
impl_Incrementable!{u8, u16, u32, u64, i8, i16, i32, i64, f32, f64}
我正在尝试了解如何在 Rust 中实现通用特征。
虽然我看过很多示例,但这些示例过于依赖特定用途(例如
相反,这是一个基于相当普遍的东西的简单示例——递增:
trait Incrementable {
fn post_inc(&mut self) -> Self;
fn post_inc_by(&mut self, n: usize) -> Self;
}
impl Incrementable for usize {
fn post_inc(&mut self) -> Self {
let tmp = *self;
*self += 1;
tmp
}
//"Overload" for full generalizability
fn post_inc_by(&mut self, n: usize) -> Self {
let tmp = *self;
*self += n;
tmp
}
}
fn main() {
let mut result = 0;
assert!(result.post_inc() == 0);
assert!(result == 1);
assert!(result.post_inc_by(3) == 1);
assert!(result == 4);
}
以上代码有效,但存在不足,因为它无法在不编写大量样板代码的情况下推广到所有数字类型。
在我尝试概括上述代码的过程中,我与类型系统发生了争执,借用了检查器,或者被迫走上了实现 FromPrimitive
我想在我的泛型中支持的每种类型的道路版本(实际上让我回到原点)。
你能帮我理解如何实现 Incrementable
吗?
这样 post_inc()
和 post_inc_by()
至少适用于所有整数和浮点类型,理想情况下不必为每种类型编写实现?
我希望答案能帮助我了解特征、实现、类型和关联类型如何在比我遇到的更直接的用例中协同工作。
我正在使用 Rust 1.16.0。
我们可以递增的类型需要
- 知道运营商和
+=
(AddAssign
) - 为 "one"-元素定义一个值
- 可复制,因为我们想保留旧的 un-incremented 值。
第 1 点和第 3 点,我们可以通过使用特征边界来确保,对于第 2 点,我们可以设置一个具有函数 one() -> self
.
所以这是一个工作示例:
// We need to know the operator "+="
use std::ops::AddAssign;
// The trait needs a type parameter
trait Incrementable<T> {
fn post_inc(&mut self) -> Self;
fn post_inc_by(&mut self, n: T) -> Self;
}
// We need a trait which tells us the "one" value for a type
trait Increment {
fn one() -> Self;
}
// We need to implement the Increment trait for every type
// we want to increment.
impl Increment for usize {
fn one() -> usize {
1
}
}
// Finally we implement the Increment trait generically for all types that
// * know the operator "+=" AddAssign
// * are copyable
// * implement our Increment trait, so that we know their "one" value
impl<T: AddAssign + Increment + Copy> Incrementable<T> for T {
fn post_inc(&mut self) -> Self {
let tmp = *self;
*self += T::one();
tmp
}
//"Overload" for full generalizability
fn post_inc_by(&mut self, n: T) -> Self {
let tmp = *self;
*self += n;
tmp
}
}
fn main() {
let mut result = 0;
assert!(result.post_inc() == 0);
assert!(result == 1);
assert!(result.post_inc_by(3) == 1);
assert!(result == 4);
}
您不必为每种类型编写 Incrementable
的实现,但您必须实现提供 one()
函数的特性。没有它就无法逃脱,因为对于非数字类型,"increment by one" 的含义并不明显。
我将所有内容都保留在可以通用实现的通用实现中。例外是 T::one()
,因此除了每个类型的一个简单函数外,不需要 boiler-plate 代码。
您可以使用宏执行此操作,遵循 what the std did:
trait Incrementable {
fn post_inc(&mut self) -> Self;
fn post_inc_by(&mut self, n: Self) -> Self;
}
macro_rules! post_inc_impl {
($($t:ty)*) => ($(
impl Incrementable for $t {
fn post_inc(&mut self) -> Self {
self.post_inc_by(1 as Self)
}
fn post_inc_by(&mut self, n: Self) -> Self {
let tmp = *self;
*self += n;
tmp
}
}
)*)
}
post_inc_impl! { usize u8 u16 u32 u64 isize i8 i16 i32 i64 f32 f64 }
fn main() {
let mut result = 0;
assert!(result.post_inc() == 0);
assert!(result == 1);
assert!(result.post_inc_by(3) == 1);
assert!(result == 4);
}
如果您使用 num
crate:
extern crate num;
use num::Num;
trait Incrementable<T: Num> {
fn post_inc(&mut self) -> Self;
fn post_inc_by(&mut self, n: T) -> Self;
}
impl<T: Num + std::ops::AddAssign<T> + Copy> Incrementable<T> for T {
fn post_inc(&mut self) -> T {
let tmp = *self;
*self += T::one();
tmp
}
fn post_inc_by(&mut self, n: T) -> Self {
let tmp = *self;
*self += n;
tmp
}
}
@Simon Whitehead's example 可以很容易地适应稳定的 Rust:
trait Incrementable: Copy + std::ops::AddAssign<Self> {
fn one() -> Self;
fn post_inc(&mut self) -> Self {
self.post_inc_by(Self::one())
}
fn post_inc_by(&mut self, n: Self) -> Self {
let tmp = *self;
*self += n;
tmp
}
}
impl Incrementable for u8 { fn one() -> Self {1} }
impl Incrementable for u16 { fn one() -> Self {1} }
impl Incrementable for u32 { fn one() -> Self {1} }
impl Incrementable for u64 { fn one() -> Self {1} }
impl Incrementable for i8 { fn one() -> Self {1} }
impl Incrementable for i16 { fn one() -> Self {1} }
impl Incrementable for i32 { fn one() -> Self {1} }
impl Incrementable for i64 { fn one() -> Self {1} }
impl Incrementable for f32 { fn one() -> Self {1.0} }
impl Incrementable for f64 { fn one() -> Self {1.0} }
虽然您需要为每种类型进行实现,但每种类型都非常简单。
您还可以使用宏来隐藏重复的实现:
macro_rules! impl_Incrementable{
($($m:ty),*) => {$( impl Incrementable for $m { fn one() -> Self { 1 as $m } })*}
}
impl_Incrementable!{u8, u16, u32, u64, i8, i16, i32, i64, f32, f64}