Skip to content

Commit 883eec8

Browse files
committed
增加新文章,《Rust模拟C++的函数重载》
1 parent aecb5bc commit 883eec8

File tree

1 file changed

+65
-0
lines changed

1 file changed

+65
-0
lines changed
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
+++
2+
title = "Rust模拟C++的函数重载"
3+
date = 2024-08-30T22:23:00-07:00
4+
lastmod = 2024-08-30T22:55:01-07:00
5+
tags = ["rust", "c++"]
6+
categories = ["rust", "c++"]
7+
draft = false
8+
toc = true
9+
+++
10+
11+
## <span class="section-num">1</span> 函数重载(function overloading) {#函数重载--function-overloading}
12+
13+
所谓的函数重载,指的是某些语言支持创建函数名相同,但函数签名不同的多个函数,所谓的函数签名,既指参数类型,也指参数的数量。 <br/>
14+
15+
如C++,Java都是支持函数重载的,而Rust是不支持函数重载的, 个人猜测可能是Rust最初的设计者认为函数重载可能会导致增加代码理解难度,尤其是在C++里面,隐式类型转换叠加函数重载,可能看代码都看不出实际调用的是哪个版本的函数。 <br/>
16+
17+
18+
## <span class="section-num">2</span> Rust版本的函数重载 {#rust版本的函数重载}
19+
20+
但是我个人觉得函数重载在大部分情况下都是很方便,也不需要为相同的函数想不同的名字,毕竟命名是编程最难的问题之一。 <br/>
21+
今天重读 Programming Rust, 2nd Edition关于 `Into` 这个trait 的功能的时候,突然意识到,可以使用 `Into` 模拟出部分的函数重载功能。 <br/>
22+
23+
为什么说是「部分」呢,因为前文提到,所谓的函数重载是指多个同名但函数签名不一样的函数,而Rust能模拟的就是参数类型不一样,但是参数数量一致的重载函数。 <br/>
24+
25+
假设我们想实现自己的 `ping` 命名, 入参可以是 [`Ipv4Addr`](https://doc.rust-lang.org/std/net/struct.Ipv4Addr.html) 这个 struct, ipv4的地址也可以使用2进制来表示, 又或者可以使用 u32 来表示,毕竟只有32位。 <br/>
26+
27+
如果用 C++, 我们可以写3个重载函数,入参分别是, `Ipv4Addr`, `bitset``uint32`. 在 Rust, 我们也实现类似的函数: <br/>
28+
29+
```rust
30+
use std::net::Ipv4Addr;
31+
fn ping<A>(address: A) -> std::io::Result<bool>
32+
where A: Into<Ipv4Addr>
33+
{
34+
let ipv4_address = address.into();
35+
...
36+
}
37+
```
38+
39+
需要注意的是,上面函数的入参并不是 `Ipv4Addr`, 而是 `Into<Ipv4Addr>=,这就是意味着,所有实现了 =Into<Ipv4Addr>` 这个 trait 的类型都可以是 `ping` 的入参,而恰好 `u32``[u8; 4]` 都实现了 =Into&lt;Ipv4Addr&gt;=,所以下面的调用都是编译通过的: <br/>
40+
41+
```rust
42+
println!("{:?}", ping(Ipv4Addr::new(23, 21, 68, 141))); // pass an Ipv4Addr
43+
println!("{:?}", ping([66, 146, 219, 98])); // pass a [u8; 4]
44+
println!("{:?}", ping(0xd076eb94_u32)); // pass a u32
45+
```
46+
47+
当然,如果你实现了 `impl From<u32> for Ipv4Addr`, Rust 编译器也会贴心地帮你把反向的 `Into<Ipv4Addr>` 也实现掉。 <br/>
48+
49+
50+
## <span class="section-num">3</span> 限制 {#限制}
51+
52+
看完上面的函数实现,有经验的朋友可能就会发现了,Rust版本的函数重载限制比C++的要多。 <br/>
53+
54+
在C++版本的函数重载中: <br/>
55+
56+
```c++
57+
void func1(Type1 foo);
58+
59+
void func1(Type2 bar);
60+
```
61+
62+
参数类型 `Type1` 和 `Type2` 并不需要存在任何关系,但是在 Rust 版本中,需要两个类型之间支持相互转换,所以可以理解成 Rust 的「函数重载」本质就是通过显示类型转换来实现的。 <br/>
63+
64+
毕竟 Rust 设计初衷之一就是支持强类型,就函数重载而言,终归聊胜于无啦。 <br/>
65+

0 commit comments

Comments
 (0)