前言
接着上篇的复数类型实例,搬砖继续。
复数类型 下
有经过上一篇的准备,我们看下现在的代码:
// complex/src/lib.rs
use std
::
ops
::
Add
;
#
[
derive
(
Default
,
Debug
,
PartialEq
,
Copy
,
Clone
)]
struct Complex
<
T
>
{
// Real part
re
:
T
,
// Complex part
im
:
T
}
impl
<
T
>
Complex
<
T
>
{
fn
new
(
re
:
T
,
im
:
T
)
->
Self
{
Complex
{
re
,
im
}
}
}
impl
<
T
:
Add
<
T
,
Output
=
T
>>
Add
for
Complex
<
T
>
{
type Output
=
Complex
<
T
>
;
fn
add
(
self
,
rhs
:
Complex
<
T
>
)
->
Self
::
Output
{
Complex
{
re
:
self
.
re
+
rhs
.
re
,
im
:
self
.
im
+
rhs
.
im
}
}
}
#
[
cfg
(
test
)]
mod tests
{
use Complex
;
#
[
test
]
fn
complex_basics
()
{
let
first
=
Complex
::
new
(
3
,
5
);
let
second
:
Complex
<
i32
>
=
Complex
::
default
();
}
fn
complex_addition
()
{
let
a
=
Complex
::
new
(
1
,
-
2
);
let
b
=
Complex
::
default
();
let
res
=
a
+
b
;
assert_eq
!
(
res
,
a
);
}
}
让我们深入了解Complex<T>的impl块:
impl
<
T
:
Add
<
T
,
Output
=
T
>
Add
for
Complex
<
T
>
相比之下,Add的impl块更加复杂,我们仔细看下:
- impl<T: Add<T, Output=T>部分说的是我们正在为一个泛型类型T实现Add,其中T实现了Add<T, Output=T>。<T, Output=T>部分表示Add trait的实现必须具有相同的输入和输出类型
- Add for Complex<T>表示我们正在为Complex<T>类型实现Add trait
- T:Add必须实现Add trait。如果没有,我们就不能对它使用+操作符
然后是From特性。如果我们也可以从内置的原语类型(比如两个元素的元组,其中第一个元素是实部,第二个元素是虚部)构造复数类型,那就方便多了。既然这么说了,其实可以通过实现From trait来做到这一点。这个特性定义了一个from方法,为我们提供了在类型之间进行转换的一般方法。其文档可以在https://doc.rust-lang.org/std/convert/trait.From.html上找到。
以下是该特性的定义:
pub trait From
<
T
>
{
fn
from
(
self
)
->
T
;
}
这个定义稍微简单一些,它是一个泛型特性,其中T指定要转换的类型。当我们实现这个时,只需要将T替换为想要实现它的类型,然后就是实现from方法。接着,我们可以在类型上使用该方法。下面是一个实现代码,它将我们的复数数值转换为一个二元元组类型,这是Rust的原生数据类型:
// complex/src/lib.rs
// previous code omitted for brevity
use std
::
convert
::
From
;
impl
<
T
>
From
<
(
T
,
T
)
>
for
Complex
<
T
>
{
fn
from
(
value
:
(
T
,
T
))
->
Complex
<
T
>
{
Complex
{
re
:
value
.
0
,
im
:
value
.
1
}
}
}
// other impls omitted
#
[
cfg
(
test
)]
mod tests
{
// other tests
use Complex
;
#
[
test
]
fn
complex_from
()
{
let
a
=
(
2345
,
456
);
let
complex
=
Complex
::
from
(
a
);
assert_eq
!
(
complex
.
re
,
2345
);
assert_eq
!
(
complex
.
im
,
456
);
}
}
让我们来看看这条impl部分(7-9行)。这类似Add trait,除了我们不必通过任何特殊的输出类型来约束泛型。第一个<T>是泛型类型T的声明,第二个和第三个是对应的用法,表明复数类型从(T, T)类型进行创建。
最后,为了让用户能够以习惯的数学符号的形式来查看复数类型,需要实现Display特性,相关文档可见https://doc.rust-lang.org/std/fmt/trait.Display.html,下面是该特性的类型签名:
pub trait Display
{
fn
fmt
(
&
self
,
&
mut Formatter
)
->
Result
<
(),
Error
>
;
}
下面的代码显示了Complex<T>类型所实现的Display:
// complex/src/lib.rs
// previous code omitted for brevity
use std
::
fmt
::{
Formatter
,
Display
,
Result
};
impl
<
T
:
Display
>
Display
for
Complex
<
T
>
{
fn
fmt
(
&
self
,
f
:
&
mut Formatter
)
->
Result
{
write
!
(
f
,
"{} + {}i"
,
self
.
re
,
self
.
im
)
}
}
#
[
cfg
(
test
)]
mod tests
{
// other tests
use Complex
;
#
[
test
]
fn
complex_display
()
{
let
my_imaginary
=
Complex
::
new
(
2345
,
456
);
println
!
(
"{}"
,
my_imaginary
);
}
}
Display特性有一个fmt方法,它接受一个Formatter 类型,我们使用write!宏。与前面一样,因为我们的Complex<T>类型对re和im字段都使用泛型类型,所以我们需要指定它也必须满足Display trait。
运行cargo test -- --nocapture,我们得到以下输出:
我们可以看到,我们的复杂类型以可读的格式打印为2345 + 456i,所有的测试都是绿色的。
结语
到这里,对标准库特性的初步探索告一段落,在下一篇里,我们看下多态(polymorphism)的内容。
主要参考和建议读者进一步阅读的文献
https://doc.rust-lang.org/book
Rust编程之道,2019, 张汉东
The Complete Rust Programming Reference Guide,2019, Rahul Sharma,Vesa Kaihlavirta,Claus Matzinger
Hands-On Data Structures and Algorithms with Rust,2018,Claus Matzinger
Beginning Rust ,2018,Carlo Milanesi
Rust Cookbook,2017,Vigneshwer Dhinakaran