4 个版本
0.1.3 | 2021 年 7 月 13 日 |
---|---|
0.1.2 | 2020 年 5 月 13 日 |
0.1.1 | 2020 年 5 月 11 日 |
0.1.0 | 2020 年 5 月 11 日 |
#1051 in Rust 模式
6,918 每月下载量
用于 12 个 Crates (5 个直接使用)
52KB
651 行
Crate tuple_list
Rust Crates 用于无宏的变长元组元编程。
提供了一种递归定义任意大小元组特质的途径。
有关详细信息和方法,请参阅 Crates 文档。
受 MIT 许可证保护。
lib.rs
:
无宏的变长元组元编程的 Crates。
原理
截至编写此 Crates,Rust 不支持变长泛型,并且不允许对元组进行一般性推理。
最重要的是,Rust 不允许为所有实现了该特质的元素的所有元组泛型实现一个特质。
此 Crates 尝试通过提供一种递归定义元组特质的方式来填补这一空白。
元组列表
元组 (A, B, C, D)
可以明确地映射到递归元组 (A, (B, (C, (D, ()))))
.
在每一级上,它由一对 (Head, Tail)
组成,其中 Head
是元组元素,Tail
是列表的其余部分。对于最后一个元素,Tail
是一个空列表。
与常规平坦元组不同,这种递归元组可以在 Rust 中有效地进行推理。
此 Crates 将此类结构称为“元组列表”,并提供了一组特性和宏,使人们可以方便地使用它们。
示例 1:PlusOne
递归特质
让我们创建一个特质,它将向任意长度元组列表的每个元素加一,根据元素类型表现不同。
// `TupleList` is a helper trait implemented by all tuple lists.
// Its use is optional, but it allows to avoid accidentally
// implementing traits for something other than tuple lists.
use tuple_list::TupleList;
// Define trait and implement it for several primitive types.
trait PlusOne {
fn plus_one(&mut self);
}
impl PlusOne for i32 { fn plus_one(&mut self) { *self += 1; } }
impl PlusOne for bool { fn plus_one(&mut self) { *self = !*self; } }
impl PlusOne for String { fn plus_one(&mut self) { self.push('1'); } }
// Now we have to implement trait for an empty tuple,
// thus defining initial condition.
impl PlusOne for () {
fn plus_one(&mut self) {}
}
// Now we can implement trait for a non-empty tuple list,
// thus defining recursion and supporting tuple lists of arbitrary length.
impl<Head, Tail> PlusOne for (Head, Tail) where
Head: PlusOne,
Tail: PlusOne + TupleList,
{
fn plus_one(&mut self) {
self.0.plus_one();
self.1.plus_one();
}
}
// `tuple_list!` as a helper macro used to create
// tuple lists from a list of arguments.
use tuple_list::tuple_list;
// Now we can use our trait on tuple lists.
let mut tuple_list = tuple_list!(2, false, String::from("abc"));
tuple_list.plus_one();
// `tuple_list!` macro also allows us to unpack tuple lists
let tuple_list!(a, b, c) = tuple_list;
assert_eq!(a, 3);
assert_eq!(b, true);
assert_eq!(&c, "abc1");
示例 2:CustomDisplay
递归特质
让我们创建一个类似于 Display
的 trait,该 trait 对所有实现了它的元组列表进行实现。
// Define the trait and implement it for several standard types.
trait CustomDisplay {
fn fmt(&self) -> String;
}
impl CustomDisplay for i32 { fn fmt(&self) -> String { self.to_string() } }
impl CustomDisplay for bool { fn fmt(&self) -> String { self.to_string() } }
impl CustomDisplay for &str { fn fmt(&self) -> String { self.to_string() } }
// Now we have to implement trait for an empty tuple,
// thus defining initial condition.
impl CustomDisplay for () {
fn fmt(&self) -> String { String::from("<empty>") }
}
// In order to avoid having trailing spaces, we need
// custom logic for tuple lists of exactly one element.
//
// The simplest way is to use `TupleList::TUPLE_LIST_SIZE`
// associated constant, but there is also another option.
//
// Instead of defining initial condition for empty tuple list
// and recursion for non-empty ones, we can define *two*
// initial conditions: one for an empty tuple list and
// one for tuple lists of exactly one element.
// Then we can define recursion for tuple lists of two or more elements.
//
// Here we define second initial condition for tuple list
// of exactly one element.
impl<Head> CustomDisplay for (Head, ()) where
Head: CustomDisplay,
{
fn fmt(&self) -> String {
return self.0.fmt()
}
}
// Recursion step is defined for all tuple lists
// longer than one element.
impl<Head, Next, Tail> CustomDisplay for (Head, (Next, Tail)) where
Head: CustomDisplay,
(Next, Tail): CustomDisplay + TupleList,
Tail: TupleList,
{
fn fmt(&self) -> String {
return format!("{} {}", self.0.fmt(), self.1.fmt());
}
}
// Ensure `fmt` is called for each element.
let tuple_list = tuple_list!(2, false, "abc");
assert_eq!(
tuple_list.fmt(),
"2 false abc",
);
// Since tuple lists implement `CustomDisplay` themselves, they can
// be elements in other tuple lists implementing `CustomDisplay`.
let nested_tuple_list = tuple_list!(2, false, "abc", tuple_list!(3, true, "def"));
assert_eq!(
nested_tuple_list.fmt(),
"2 false abc 3 true def",
);
示例 3: SwapStringAndInt
递归 trait
让我们实现一个 trait,该 trait 将 i32
转换为 String
,反之亦然。
这个示例比其他示例要复杂得多,因为它将一个元组列表映射到另一个元组列表。
// Let's define and implement a trait for `i32` and `String`
// so that it converts `String` to `i32` and vice versa.
trait SwapStringAndInt {
type Other;
fn swap(self) -> Self::Other;
}
impl SwapStringAndInt for i32 {
type Other = String;
fn swap(self) -> String { self.to_string() }
}
impl SwapStringAndInt for String {
type Other = i32;
fn swap(self) -> i32 { self.parse().unwrap() }
}
// Now we have to implement trait for an empty tuple,
// thus defining initial condition.
impl SwapStringAndInt for () {
type Other = ();
fn swap(self) -> () { () }
}
// Now we can implement trait for a non-empty tuple list,
// thus defining recursion and supporting tuple lists of arbitrary length.
impl<Head, Tail, TailOther> SwapStringAndInt for (Head, Tail) where
Head: SwapStringAndInt,
Tail: SwapStringAndInt<Other=TailOther> + TupleList,
TailOther: TupleList,
{
type Other = (Head::Other, Tail::Other);
fn swap(self) -> Self::Other {
(self.0.swap(), self.1.swap())
}
}
// Tuple lists implement `SwapStringAndInt` by calling `SwapStringAndInt::swap`
// on each member and returning tuple list of resulting values.
let original = tuple_list!(4, String::from("2"), 7, String::from("13"));
let swapped = tuple_list!(String::from("4"), 2, String::from("7"), 13);
assert_eq!(original.swap(), swapped);
示例 4: 预先添加和附加函数
让我们为元组列表实现附加和预先添加函数。
// Prepend is a trivial operation for tuple lists.
// We just create a new pair from prepended element
// and the remainder of the list.
fn prepend<T, Tail: TupleList>(value: T, tail: Tail) -> (T, Tail) {
(value, tail)
}
// Append is a bit more comples. We'll need a trait for that.
trait Append<T>: TupleList {
type AppendResult: TupleList;
fn append(self, value: T) -> Self::AppendResult;
}
// Implement append for an empty tuple list.
impl<T> Append<T> for () {
type AppendResult = (T, ());
// Append for an empty tuple list is quite trivial.
fn append(self, value: T) -> Self::AppendResult { (value, ()) }
}
// Implement append for non-empty tuple list.
impl<Head, Tail, T> Append<T> for (Head, Tail) where
Self: TupleList,
Tail: Append<T>,
(Head, Tail::AppendResult): TupleList,
{
type AppendResult = (Head, Tail::AppendResult);
// Here we deconstruct tuple list,
// recursively call append on the
// tail of it, and then reconstruct
// the list using the new tail.
fn append(self, value: T) -> Self::AppendResult {
let (head, tail) = self;
return (head, tail.append(value));
}
}
// Now we can use our append and prepend functions
// on tuple lists.
let original = tuple_list!( 1, "foo", false);
let appended = tuple_list!( 1, "foo", false, 5);
let prepended = tuple_list!(5, 1, "foo", false);
assert_eq!(original.append(5), appended);
assert_eq!(prepend(5, original), prepended);
示例 5: 反转函数
我们还可以实现一个函数,该函数可以反转元组列表中的元素。
// Rewind is a helper trait which maintains two tuple lists:
// `Todo` (which is `Self` for the trait) is the remainder of a tuple list to be reversed.
// `Done` is already reversed part of it.
trait Rewind<Done: TupleList> {
// RewindResult is the type of fully reversed tuple.
type RewindResult: TupleList;
fn rewind(self, done: Done) -> Self::RewindResult;
}
// Initial condition.
impl<Done: TupleList> Rewind<Done> for () {
type RewindResult = Done;
// When nothing is left to do, just return reversed tuple list.
fn rewind(self, done: Done) -> Done { done }
}
// Recursion step.
impl<Done, Next, Tail> Rewind<Done> for (Next, Tail) where
Done: TupleList,
(Next, Done): TupleList,
Tail: Rewind<(Next, Done)> + TupleList,
{
type RewindResult = Tail::RewindResult;
// Strip head element from `Todo` and prepend it to `Done` list,
// then recurse on remaining tail of `Todo`.
fn rewind(self, done: Done) -> Self::RewindResult {
let (next, tail) = self;
return tail.rewind((next, done));
}
}
// Helper function which uses `Rewind` trait to reverse a tuple list.
fn reverse<T>(tuple: T) -> T::RewindResult where
T: Rewind<()>
{
// Initial condition, whole tuple list is `Todo`,
// empty tuple is `Done`.
tuple.rewind(())
}
// Now `reverse` is usable on tuple lists.
let original = tuple_list!(1, "foo", false);
let reversed = tuple_list!(false, "foo", 1);
assert_eq!(reverse(original), reversed);
元组列表和元组互操作性
此 crate 定义了 Tuple
和 TupleList
trait,这些 trait 可以自动实现,允许您将元组转换为元组列表,反之亦然。
处理互操作性的最佳方式是将数据存储为元组列表,并在必要时将其转换为元组。
或者,可以创建一个辅助函数,该函数接受一个元组,将其转换为元组列表,调用 trait 方法,然后返回结果。
以下是一个从上一个示例中的 Append
trait 创建的此类函数的示例
// `Tuple` trait is needed to access conversion function.
use tuple_list::Tuple;
fn append<T, AppendedTupleList, Elem>(tuple: T, elem: Elem) -> AppendedTupleList::Tuple where
T: Tuple, // input argument tuple
T::TupleList: Append<Elem, AppendResult=AppendedTupleList>, // input argument tuple list
AppendedTupleList: TupleList, // resulting tuple list
{
// Convert tuple into tuple list, append the element
// and convert the result back into tuple.
tuple.into_tuple_list().append(elem).into_tuple()
}
// Unlike `Append` trait which is defined for tuple lists,
// `append` function works on regular tuples.
let original = (1, "foo", false);
let appended = (1, "foo", false, 5);
assert_eq!(append(original, 5), appended);
请注意,元组/元组列表转换是破坏性的,它会消耗原始数据,这看起来阻止您修改原始元组的内容。
为了减轻这个问题,tuple_list
crate 引入了 AsTupleOfRefs
trait,该 trait 允许将引用转换为引用的元组。
想法是,如果您可以将引用转换为引用的元组,然后可以将引用的元组转换为元组列表,然后像往常一样使用递归 trait。
让我们修改 PlusOne
trait 示例,以便它可以用于修改常规元组
// Define trait and implement it for several primitive types.
trait PlusOne {
fn plus_one(&mut self);
}
impl PlusOne for i32 { fn plus_one(&mut self) { *self += 1; } }
impl PlusOne for bool { fn plus_one(&mut self) { *self = !*self; } }
impl PlusOne for String { fn plus_one(&mut self) { self.push('1'); } }
// Now we have to define a new trait
// specifically for tuple lists of references.
//
// Unlike the original, it accepts `self` by value.
trait PlusOneTupleList: TupleList {
fn plus_one(self);
}
// Now we have to implement trait for an empty tuple,
// thus defining initial condition.
impl PlusOneTupleList for () {
fn plus_one(self) {}
}
// Now we can implement trait for a non-empty tuple list,
// thus defining recursion and supporting tuple lists of arbitrary length.
//
// Note that we're implementing `PlusOneTupleList` for
// *tuple list of mutable references*, and as a result
// head of the list is a mutable reference, not a value.
impl<Head, Tail> PlusOneTupleList for (&mut Head, Tail) where
Self: TupleList,
Head: PlusOne,
Tail: PlusOneTupleList,
{
fn plus_one(self) {
self.0.plus_one();
self.1.plus_one();
}
}
// Now let's define a helper function operating on regular tuples.
fn plus_one<'a, T, RT>(tuple: &'a mut T) where
T: AsTupleOfRefs<'a, TupleOfMutRefs=RT>,
RT: Tuple + 'a,
RT::TupleList: PlusOneTupleList,
{
tuple.as_tuple_of_mut_refs().into_tuple_list().plus_one()
}
// Now we can use this helper function on regular tuples.
let mut tuple = (2, false, String::from("abc"));
plus_one(&mut tuple);
assert_eq!(tuple.0, 3);
assert_eq!(tuple.1, true);
assert_eq!(&tuple.2, "abc1");
如您所见,使用元组需要很多样板代码。除非您需要支持现有的代码,否则通常最好直接使用元组列表,因为它们更容易处理。
为常规元组实现递归 trait
为常规元组实现递归 trait 会带来某些问题。到目前为止,在 tuple_list
crate 中是可能的,但使用外部它时,会迅速导致孤儿规则违规。
您可以在 tuple_list::test::all_features
中看到一个为常规元组实现 trait 的工作示例,但它过于复杂,基本上是实验性的。
一旦 Rust 中实现了特征专业化功能,就可以在常规元组上定义递归特征。