glossary
- ingress, 入站
- egress, 出站
- repr: representation
- cfg: configuration
pub crate
1
|
pub(crate) const DISCOVERY_SILENT_TIME: Duration = Duration::from_millis(1_000);
|
在你的代码中,
pub(crate) const DISCOVERY_SILENT_TIME: Duration = Duration::from_millis(1_000);
表示 DISCOVERY_SILENT_TIME 常量在定义它的 crate 内部是可用的,但在其他 crate 中是不可用的。
这样可以帮助你控制 API 的可见性,确保只有内部代码可以使用某些功能。
struct in enum
1
2
3
4
5
6
7
8
9
10
11
|
enum NeighborState {
/// Socket can be polled immediately.
#[default]
Active,
/// Socket should not be polled until either `silent_until` passes or
/// `neighbor` appears in the neighbor cache.
Waiting {
neighbor: IpAddress, // size=0x17
silent_until: Instant, // size=0x08
},
}
|
这里的 Waiting 相当于是:NeighborState 中定义的 private 的 struct.
match if
1
2
3
4
5
|
match self.neighbor_state {
NeighborState::Active => socket_poll_at,
NeighborState::Waiting { neighbor, .. } if has_neighbor(neighbor) => socket_poll_at,
NeighborState::Waiting { silent_until, .. } => PollAt::Time(silent_until),
}
|
这里看似很矛盾的一种情况:Waiting 有两条匹配,但是仔细看一下,the second pattern has an if
,
if true, then: choose the second pattern, else choose the last pattern.
Socket
1
2
3
4
5
6
|
pub enum Socket<'a> {
Raw(raw::Socket<'a>),
Icmp(icmp::Socket<'a>),
Udp(udp::Socket<'a>),
Tcp(tcp::Socket<'a>),
}
|
icmp::Socket
icmp, internet control message protocol.
endpoint: 一个特定的网络地址和端口的组合,用于表示一个通信的终点。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
pub struct Socket<'a> {
rx_buffer: PacketBuffer<'a>,
tx_buffer: PacketBuffer<'a>,
/// The endpoint this socket is communicating with
endpoint: Endpoint,
/// The time-to-live (IPv4) or hop limit (IPv6) value used in outgoing packets.
hop_limit: Option<u8>,
// rx_waker: WakerRegistration,
// tx_waker: WakerRegistration,
}
pub enum Endpoint {
#[default] Unspecified,
Ident(u16),
Udp(IpListenEndpoint),
}
pub struct ListenEndpoint {
pub addr: Option<Address>,
pub port: u16,
}
pub enum Address {
Ipv4(Ipv4Address),
Ipv6(Ipv6Address),
}
// defined in core::net
pub struct Ipv4Addr {
octets: [u8; 4],
}
|
icmp::PacketBuffer
1
2
|
/// An ICMP packet ring buffer.
pub type PacketBuffer<'a> = crate::storage::PacketBuffer<'a, IpAddress>;
|
crate::storage::PacketBuffer
1
2
3
4
5
|
/// An UDP packet ring buffer.
pub struct PacketBuffer<'a, H: 'a> {
metadata_ring: RingBuffer<'a, PacketMetadata<H>>,
payload_ring: RingBuffer<'a, u8>,
}
|
这里 H 是一个泛型参数,表示 H 必须与生命周期 ‘a 相关联。
在这个 icmp 例子中,H 就是 IpAddress,
这也就是以为这一条约束:IpAddress 的生命周期必须比 PacketBuffer 长 (包含相等情况).
在这种情况下是平凡的,因为 IpAddress 实现了 Copy,
这意味着在创建 PacketBuffer 结构体的时候,IpAddress 复制了一份,
PacketBuffer 拥有新创建的 IpAddress,
因此 IpAddress 的生命周期应该与 PacketBuffer 的生命周期是一样长的,这满足约束。
我们还可以进行推广:只要 H 是所有权传递的,那么约束自然会被满足。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
pub struct PacketMetadata<H> {
size: usize,
header: Option<H>,
}
impl<H> PacketMetadata<H> {
// ...
fn packet(size: usize, header: H) -> PacketMetadata<H> {
PacketMetadata {
size: size,
header: Some(header),
}
}
// ...
}
impl<'a, H> PacketBuffer<'a, H> {
/// Enqueue a single packet with the given header into the buffer, and
/// return a reference to its payload, or return `Err(Full)`
/// if the buffer is full.
pub fn enqueue(&mut self, size: usize, header: H) -> Result<&mut [u8], Full> {
// ...
*self.metadata_ring.enqueue_one()? = PacketMetadata::packet(size, header);
// ...
}
}
|
maches!
1
2
3
|
if matches!(self.caps.medium, Medium::Ethernet) {
total_len = EthernetFrame::<&[u8]>::buffer_len(total_len);
}
|
equales to below
1
2
3
4
5
6
|
if match (self.caps.medium) {
Medium::Ethernet => true,
_ => false,
} {
total_len = EthernetFrame::<&[u8]>::buffer_len(total_len);
}
|
其中,typeof(medium) = Medium, which is a enum contains Medium::Ethernet variant
associated type
关联类型。
1
2
3
4
5
6
7
8
|
// smoltcp/src/phy/mod.rs
pub trait Device {
type RxToken<'a>: RxToken where Self: 'a;
type TxToken<'a>: TxToken where Self: 'a;
fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>;
fn transmit(&mut self, timestamp: Instant) -> Option<Self::TxToken<'_>>;
fn capabilities(&self) -> DeviceCapabilities;
}
|
need to fill the RxToken in impl
.
A typical example is: TryFrom, which we should fill the Error in out impl.
这里值得注意的是:type RxToken<'a>: RxToken where Self: 'a
中的 'a
应该是来自于 impl 的
声明周期换名
这里有生命周期:‘a, 其实这里应该是有两种生命周期,下面我们来换个名
1
2
3
4
5
6
7
8
|
// smoltcp/src/phy/mod.rs
pub trait Device {
type RxToken<'a>: RxToken where Self: 'a;
type TxToken<'b>: TxToken where Self: 'b;
fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>;
fn transmit(&mut self, timestamp: Instant) -> Option<Self::TxToken<'_>>;
fn capabilities(&self) -> DeviceCapabilities;
}
|
生命周期推断
1
|
fn receive(&mut self, timestamp: Instant) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)>;
|
这里的两个 '_
其实可以推断出来,来自于 &self
tuntap
1
2
3
4
5
6
7
8
9
10
11
|
pub struct RxToken {
buffer: Vec<u8>,
}
pub struct TxToken {
lower: Rc<RefCell<sys::TunTapInterfaceDesc>>,
}
impl Device for TunTapInterface {
type RxToken<'a> = RxToken;
type TxToken<'a> = TxToken;
// ...
}
|
可见,RxToken, TxToken 并没有生命周期的约束,这种情况是平凡的。
拍案:没懂,感觉 生命周期比 xx 长,这个概念比较奇怪,可能还是 “生命周期有关联” 这个说法更好一些。
configuration
1
2
3
4
|
#[cfg(all(
any(feature = "phy-raw_socket", feature = "phy-tuntap_interface"),
unix
))]
|
all, any, 一种连词。
发现 cfg 只是 configuration 的意思,我还以为是 conditional xxx (条件编译).
闭包
Fn
FnOnce
FnMut
1
2
3
|
let mut lambda = | xx : T | {
// ...
};
|
Range
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// smoltcp/src/wire/mod.rs
mod field {
pub type Field = ::core::ops::Range<usize>;
pub type Rest = ::core::ops::RangeFrom<usize>;
}
// example
mod field {
use crate::wire::field::*;
pub const DESTINATION: Field = 0..6;
pub const SOURCE: Field = 6..12;
pub const ETHERTYPE: Field = 12..14;
pub const PAYLOAD: Rest = 14..;
}
|
注意到一个有趣的事情:import 的时候使用的是绝对路径。
AsRef
1
2
3
4
5
6
7
8
9
|
// src/rust/library/core/convert/mod.rs
pub trait AsRef<T: ?Sized> {
fn as_ref(&self) -> &T;
}
// smoltcp/src/wire/ethernet.rs
pub struct Frame<T: AsRef<[u8]>> {
buffer: T,
}
|
non_exhaustive
1
2
3
4
5
6
|
#[cfg_attr(feature = "defmt", derive(defmt::Format))]
#[derive(Debug, PartialEq, Eq, Hash, Clone, Copy, Default)]
#[non_exhaustive]
pub struct PacketMeta {
pub id: u32,
}
|
the struct/enum tagged with #[non_exhaustive]
means:
we COULDNOT construct the data in this way:
1
2
3
|
let meta = PacketMet {
id: xxx
};
|
we should use Default construct and modify the fields instead:
1
2
|
let mut meta = PacketMeta::default();
meta.id = xxx;
|
macro_use
发现了一个操蛋的事情。这可能与 rust 编译器实现有关,顺序扫描几遍…, sad.
如果 #[macro_use] mod macros;
与 pub mod phy;
互换位置,
那么在引用了宏的地方就会报错。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
//# src/lib.rs
#[macro_use]
mod macros; // this should be previous than the mod phy; fuck rust
// ...
pub mod phy;
//# src/macros.rs
macro_rules! net_log {
(trace, $($arg:expr),*) => { println!($($arg),*) };
(debug, $($arg:expr),*) => { println!($($arg),*) };
}
macro_rules! net_debug {
($($arg:expr),*) => (net_log!(debug, $($arg),*));
}
//# src/phy/tuntap_interface.rs
impl phy::TxToken for TxToken {
fn consume<R, F>(self, len: usize, f: F) -> R
where
F: FnOnce(&mut [u8]) -> R,
{
// ...
net_debug!("phy: tx failed due to WouldBlock");
// ...
}
}
|
高阶函数
拍案: 发现 smoltcp 有许多这种: 传入一个函数处理的例子, 比方说 update_ip_addrs.
传入一个函数指针, 然后让函数指针处理列表的内容.
1
2
3
4
5
6
|
impl Interface {
pub fn update_ip_addrs<F: FnOnce(&mut Vec<IpCidr>)>(&mut self, f: F) {
f(&mut self.inner.ip_addrs); // update ip_addrs
// ...
}
}
|
haskell 中这种高阶函数更是常见
1
2
3
4
|
zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith _ _ [] = []
zipWith _ [] _ = []
zipWith f (x:xs) (y:ys) = f x y : zipWith f xs ys
|
smoltcp/src/phy
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
● [wangfiox@gentoo] in phy ± |main ✗|
➜ tree .
.
├── fault_injector.rs // 故障注入, 用于模拟丢包的情况, for test
├── fuzz_injector.rs // 模糊测试注入, for test
├── loopback.rs
├── mod.rs
├── pcap_writer.rs // 用于数据包捕获的设备, 将网络数据包以 libpcap 格式写入到一个输出目标
├── raw_socket.rs
├── sys
│ ├── bpf.rs
│ ├── linux.rs
│ ├── mod.rs
│ ├── raw_socket.rs
│ └── tuntap_interface.rs
├── tracer.rs
└── tuntap_interface.rs
|
这个
struct size=0
used for tag
1
2
3
4
5
6
7
8
9
10
|
//# smoltcp/src/iface/route.rs
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct RouteTableFull;
impl core::fmt::Display for RouteTableFull {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "Route table full")
}
}
|
defmt