泛型的泛型
Generics of Generic types
我打算在用 C++ 编程后开始学习 Rust。我不确定如何创建一个将泛型类型作为其模板参数的函数(或任何其他泛型)。
我试过编译以下代码:
trait Monad {
fn singleton<T>(t: T) -> Self<T>;
fn compact<T>(mmt: Self<Self<T>>) -> Self<T>;
}
fn id<F>(fi: F<i8>) -> F<i8> {return fi;}
但是这会吐出一堆 type argument not allowed
错误。
在 C++20 中我会这样写:
template<typename<typename> M>
concept monad = requires(...){...};
template<typename<typename> F>
F<std::byte> id(F<std::byte> fi) {return fi;}
我如何在 Rust 中实现这个功能?
模板模板参数是高级类型的有限形式。
Rust 也有一种有限形式的高级类型,以“通用关联类型”的形式出现。这些都是每晚提供的。具体来说,Monad
示例可能如下所示:
#![feature(generic_associated_types)]
trait Monad {
type Type<T>;
fn singleton<T>(t: T) -> Self::Type<T>;
fn compact<T>(mmt: Self::Type<Self::Type<T>>) -> Self::Type<T>;
}
struct Vector;
impl Monad for Vector {
type Type<T> = Vec<T>;
fn singleton<T>(t: T) -> Self::Type<T> {
vec![t]
}
fn compact<T>(mmt: Self::Type<Self::Type<T>>) -> Self::Type<T> {
mmt.into_iter().flatten().collect()
}
}
在特征中,Self
始终是具体类型而不是类型构造函数。因为只有具体类型才能满足特征。这就是 Self<T>
不被接受的原因。
为了与 C++ 进行比较和对比,在 Rust 中你不能引用 Vec
而没有它的类型参数作为类型构造函数。 std::vector
本身在 C++ 中是可命名的。因此,我们必须在 Rust 中使用 Vector
来替代它。此外,我们的 Rust 实现使用了不稳定的语言功能。
另一方面,您很难用 C++ 完成 monad
概念的编写。模板模板参数不能应用于任何类型。类型构造函数是偏向的,而偏向是隐式的。对 monad 的合理要求可能是它可以用任何 std::movable
类型实例化,并且函数是为 std::movable
类型参数定义的。这在 C++ 中是无法表达的。
所以,在 C++ 中,你可能会这样写:
struct movable {
movable() = delete;
movable(const movable&) = delete;
movable(movable&&) noexcept = default;
movable& operator=(const movable&) = delete;
movable& operator=(movable&&) noexcept = default;
~movable() = default;
};
template<template<typename> typename M>
struct type {};
template<template<typename> typename M>
concept monad = requires {
{ singleton(type<M>{}, movable{}) } -> std::same_as<M<movable>>;
// compact is similar
};
在 C++ 中,您不能说约束适用于特定形状的所有类型的实例化(例如 std::movable
)。因此,您创建了一个具有该形状的类型,仅此而已,并且要求约束保持在 just 那个实例化。人们习惯称这些类型为“原型”。您希望没有特化、重载或其他可能阻止这种满足感泛化到该形状的所有类型的东西。
然后,您可以选择函数所在的位置。模板模板参数不能有成员函数,所以你要么把它们放在一个特定的实例化中(例如 M<T>
可以从 T
构造)或者作为一个自由函数。使用成员函数有缺点,因为它不能在外部实现。使用自由函数,您需要一种识别 monad 的方法,因此您最终也将模板模板参数包装在替代类型中。
比较这些很有趣。
这些称为高级类型或类型构造函数。
Rust 目前没有这些,对该声明有两个警告。
很快就会有泛型关联类型之类的东西,它们很相似,但又不完全相同:
trait Foo {
type OutputCollection<T>;
fn get_collection<T>(&self) -> Self::OutputCollection<T>;
}
您可以使用辅助特征来模拟它们,但它看起来并不好看:
trait ContainerHelper<Container> {
type Container;
}
struct VecContainer;
impl<T> ContainerHelper<VecContainer> for T {
type Container = Vec<T>;
}
trait Foo {
fn get_collection<T, Container>(&self) ->
<T as ContainerHelper<Container>>::Container
where
T: ContainerHelper<Container>;
}
我打算在用 C++ 编程后开始学习 Rust。我不确定如何创建一个将泛型类型作为其模板参数的函数(或任何其他泛型)。
我试过编译以下代码:
trait Monad {
fn singleton<T>(t: T) -> Self<T>;
fn compact<T>(mmt: Self<Self<T>>) -> Self<T>;
}
fn id<F>(fi: F<i8>) -> F<i8> {return fi;}
但是这会吐出一堆 type argument not allowed
错误。
在 C++20 中我会这样写:
template<typename<typename> M>
concept monad = requires(...){...};
template<typename<typename> F>
F<std::byte> id(F<std::byte> fi) {return fi;}
我如何在 Rust 中实现这个功能?
模板模板参数是高级类型的有限形式。
Rust 也有一种有限形式的高级类型,以“通用关联类型”的形式出现。这些都是每晚提供的。具体来说,Monad
示例可能如下所示:
#![feature(generic_associated_types)]
trait Monad {
type Type<T>;
fn singleton<T>(t: T) -> Self::Type<T>;
fn compact<T>(mmt: Self::Type<Self::Type<T>>) -> Self::Type<T>;
}
struct Vector;
impl Monad for Vector {
type Type<T> = Vec<T>;
fn singleton<T>(t: T) -> Self::Type<T> {
vec![t]
}
fn compact<T>(mmt: Self::Type<Self::Type<T>>) -> Self::Type<T> {
mmt.into_iter().flatten().collect()
}
}
在特征中,Self
始终是具体类型而不是类型构造函数。因为只有具体类型才能满足特征。这就是 Self<T>
不被接受的原因。
为了与 C++ 进行比较和对比,在 Rust 中你不能引用 Vec
而没有它的类型参数作为类型构造函数。 std::vector
本身在 C++ 中是可命名的。因此,我们必须在 Rust 中使用 Vector
来替代它。此外,我们的 Rust 实现使用了不稳定的语言功能。
另一方面,您很难用 C++ 完成 monad
概念的编写。模板模板参数不能应用于任何类型。类型构造函数是偏向的,而偏向是隐式的。对 monad 的合理要求可能是它可以用任何 std::movable
类型实例化,并且函数是为 std::movable
类型参数定义的。这在 C++ 中是无法表达的。
所以,在 C++ 中,你可能会这样写:
struct movable {
movable() = delete;
movable(const movable&) = delete;
movable(movable&&) noexcept = default;
movable& operator=(const movable&) = delete;
movable& operator=(movable&&) noexcept = default;
~movable() = default;
};
template<template<typename> typename M>
struct type {};
template<template<typename> typename M>
concept monad = requires {
{ singleton(type<M>{}, movable{}) } -> std::same_as<M<movable>>;
// compact is similar
};
在 C++ 中,您不能说约束适用于特定形状的所有类型的实例化(例如 std::movable
)。因此,您创建了一个具有该形状的类型,仅此而已,并且要求约束保持在 just 那个实例化。人们习惯称这些类型为“原型”。您希望没有特化、重载或其他可能阻止这种满足感泛化到该形状的所有类型的东西。
然后,您可以选择函数所在的位置。模板模板参数不能有成员函数,所以你要么把它们放在一个特定的实例化中(例如 M<T>
可以从 T
构造)或者作为一个自由函数。使用成员函数有缺点,因为它不能在外部实现。使用自由函数,您需要一种识别 monad 的方法,因此您最终也将模板模板参数包装在替代类型中。
比较这些很有趣。
这些称为高级类型或类型构造函数。
Rust 目前没有这些,对该声明有两个警告。
很快就会有泛型关联类型之类的东西,它们很相似,但又不完全相同:
trait Foo { type OutputCollection<T>; fn get_collection<T>(&self) -> Self::OutputCollection<T>; }
您可以使用辅助特征来模拟它们,但它看起来并不好看:
trait ContainerHelper<Container> { type Container; } struct VecContainer; impl<T> ContainerHelper<VecContainer> for T { type Container = Vec<T>; } trait Foo { fn get_collection<T, Container>(&self) -> <T as ContainerHelper<Container>>::Container where T: ContainerHelper<Container>; }