#tuple #array #vector #bit-vector #push #iterator #little

ordes

一个将数组(arrays)和元组(tuples)处理得更像向量的crate。

12 个版本

0.4.0 2024年7月3日
0.3.4 2023年3月28日
0.3.3 2022年12月30日
0.3.2 2022年6月19日
0.1.0 2021年4月13日

#819Rust 模式

Download history 97/week @ 2024-06-28 22/week @ 2024-07-05 48/week @ 2024-07-26 6/week @ 2024-08-02

54 每月下载次数

EUPL-1.2

31KB
663

Ordes

一个将数组(arrays)和元组(tuples)处理得更像向量的crate。

目前,此crate提供了通过 .pop().rest().push(val).cons(val) 方法通过 OrdesPopOrdesRestOrdesPushOrdesCons traits 分别实现,并额外提供了通过 .split().concat(arr).remove().insert(val) 方法通过 OrdesSplitOrdesConcatOrdesRemoveOrdesInsert traits 分别实现的通过 const_generics crate 功能,这需要一个 nightly 编译器和启用了不完整的 generic_const_exprs 功能。这四个 trait 只是通过这个功能提供和实现的,因为每个单独的一个都需要非线性扩展的 trait 实现数量,而且我不确定有人愿意忍受那些编译时间。同样,它们也只适用于数组。

这个crate的诞生源于我固执地想要在可能的情况下使用迭代器而不是 for 循环,这使得数据打包有时变得有些痛苦。例如,考虑一个生成所有小写四字母“单词”的迭代器。

('a'..='z')
    .flat_map(|l1| ('a'..='z').map(move |l2| (l1, l2)))
    .flat_map(|(l1, l2)| ('a'..='z').map(move |l3| (l1, l2, l3)))
    .flat_map(|(l1, l2, l3)| ('a'..='z').map(move |l4| (l1, l2, l3, l4)))
    .for_each(|(l1, l2, l3, l4)| println!("{}{}{}{}", l1, l2, l3, l4));

如我们所见,这既是

  1. 并不特别令人愉快地编写或查看
  2. 一个理智的人不会做的事情

这个crate提供了一个替代方案

use ordes::OrdesPush;
('a'..='z')
    .flat_map(|l1| ('a'..='z').map(move |l2| (l1, l2)))
    .flat_map(|chars| ('a'..='z').map(move |l3| chars.push(l3)))
    .flat_map(|chars| ('a'..='z').map(move |l4| chars.push(l4)))
    .for_each(|(l1, l2, l3, l4)| println!("{}{}{}{}", l1, l2, l3, l4));

这里并没有发生真正的魔法。代码.push(val)生成一个新类型的数据。在第一个例子中,代码chars.push(l3),输入类型是(char, char),输出是(char, char, char)。对于代码chars.push(l4)也是类似的——输入类型为(char, char, char),输出为(char, char, char, char)。使用数组也可以实现几乎相同的实现。

use ordes::OrdesPush;
('a'..='z')
    .flat_map(|l1| ('a'..='z').map(move |l2| [l1, l2]))
    .flat_map(|chars| ('a'..='z').map(move |l3| chars.push(l3)))
    .flat_map(|chars| ('a'..='z').map(move |l4| chars.push(l4)))
    .for_each(|[l1, l2, l3, l4]| println!("{}{}{}{}", l1, l2, l3, l4));

在这种情况下,代码chars.push(l3)接收[char; 2]并生成[char; 3],而代码chars.push(l4)接收[char; 3]并生成[char; 4]

最近出现的一个愚蠢的用例让我添加了两个新的特性——《OrdesRest》和《OrdesCons`——即在一个固定大小的数组前加上一个指示字节,然后将其序列化到字节流中。

use std::net::IpAddr;
use ordes::OrdesCons;

fn ipaddr_bytestream(addr: IpAddr) -> impl Iterator<Item = u8> {
    let mut data = [0; 17];
    let len = match addr {
        IpAddr::V4(addr) => {
            data[0..5].copy_from_slice(&addr.octets().cons(4));
            5
        },
        IpAddr::V6(addr) => {
            data[0..17].copy_from_slice(&addr.octets().cons(6));
            17
        }
    };
    data.into_iter().take(len)
}

如果没有.cons(_),在每个分支中都需要再添加一行代码来将46写入到data[0]中。这是一个小小的改进,但我还是觉得很有价值。

在这个包中,我为数组实现了《OrdesPop`,《OrdesRest`,《OrdesPush`和《OrdesCons`,但有一些限制。

  • 在稳定版中,所有特性仅针对长度最多为32的数组实现(默认情况下,有最多支持到1024的功能)。
  • 在夜间版和稳定版中,所有特性仅针对最多32种类型的元组实现(默认情况下,有最多支持到1024的功能)。

此crate暴露的所有特性方法都消耗self并产生一个更长或更短的类型(在元组的情况下,可能还会添加或删除最后一个类型)。我非常清楚这是一个非常有限的使用场景,但对我很有用,也许对其他一些人也有用。

关于const_generics功能的说明

为了使用此crate的const_generics功能,您还必须在crate中某处放置以下代码(例如lib.rsmain.rs等):#![feature(generic_const_exprs)]。如果未启用此功能,则依赖于ConstCheck<B>的此crate中的任何特性实现都将失败。

依赖项

~1.5MB
~36K SLoC