6.2 通用类型 (Generic Types)

基本 (Basics)

In Sway, generic types follow a very similar pattern to those in Rust. Let's look at some example syntax, starting with a generic function:

在Sway中,通用类型与Rust中的模式非常相似。让我们看看一些例子的语法,从一个通用函数开始:

fn noop<T>(argument: T) -> T {
    argument
}

Here, the noop() function trivially returns exactly what was given to it. T is a type parameter, and it says that this function exists for all types T. More formally, this function could be typed as:

在这里,noop()函数单纯地返回给它的东西。T是一个 类型参数,它说这个函数对所有类型的T都存在。更正式地说,这个函数可以被写成:

noop :: ∀T. T -> T

Generic types are a way to refer to types in general, meaning without specifying a single type. Our noop function would work with any type in the language, so we don't need to specify noop(argument: u8) -> u8, noop(argument: u16) -> u16, etc.

一般来说,通用类型是一种指代类型的方式,意思是不需要指定单一类型。我们的 noop函数对语言中的任何类型都有效,所以我们不需要指定 noop(参数: u8) -> u8noop(参数: u16) -> u16,等等。

代码生成 (Code Generation)

One question that arises when dealing with generic types is: how does the assembly handle this? There are a few approaches to handling generic types at the lowest level. Sway uses a technique called monomorphization. This means that the generic function is compiled to a non-generic version for every type it is called on. In this way, generic functions are purely shorthand for the sake of ergonomics.

在处理通用类型时,出现的一个问题是:汇编如何处理这个问题?有几种方法可以在最底层处理通用类型。Sway使用一种叫做单形态化 monomorphization的技术。这意味着,对于每一个被调用的类型,泛型函数被编译成非泛型版本。这样一来,泛型函数就纯粹是为了人机工程学的目的而进行的速记。

特质约束 (Trait Constraints)

Note Trait constraints have not yet been implemented 注意特质约束尚未实现

Important background to know before diving into trait constraints is that the where clause can be used to specify the required traits for the generic argument. So, when writing something like a HashMap you may want to specify that the generic argument implements a Hash trait.

在深入研究特质约束之前,需要了解的重要背景是,where子句可以用来指定通用参数所需的特质。因此,当编写像 HashMap这样的东西时,你可能想指定泛型参数实现Hash特质。

fn get_hashmap_key<T>(Key : T) -> b256
    where T: Hash
{
    // Code within here can then call methods associated with the Hash trait on Key
}

Of course, our noop() function is not useful. Often, a programmer will want to declare functions over types which satisfy certain traits. For example, let's try to implement the successor function, successor(), for all numeric types.

当然,我们的noop()函数是没有用的。通常情况下,程序员会想在满足某些特征的类型上声明函数。例如,让我们尝试为所有数字类型实现继任函数successor()

fn successor<T>(argument: T)
    where T: Add
{
    argument + 1
}

Run forc build, and you will get:

运行forc build,你会得到:

.. |
 9 |   where T: Add
10 |   {
11 |       argument + 1                                        
   |                  ^ Mismatched types: expected type "T" but saw type "u64"
12 |   }
13 |

This is because we don't know for a fact that 1, which in this case defaulted to 1u64, actually can be added to T. What if T is f64? Or b256? What does it mean to add 1u64 in these cases?

这是因为我们不知道1,在这种情况下默认为1u64,实际上可以添加到T。如果Tf64呢?或者b256?在这些情况下,添加1u64是什么意思?

We can solve this problem with another trait constraint. We can only find the successor of some value of type T if that type T defines some incrementor. Let's make a trait:

我们可以用另一个特征约束来解决这个问题。我们只有在T类型定义了一些递增器的情况下才能找到T类型的某个值的继承者。让我们做一个特质:

trait Incrementable {
    /// Returns the value to add when calculating the successor of a value.
    fn incrementor() -> Self;
}

Now, we can modify our successor() function:

现在,我们可以修改我们的successor()函数:

fn successor<T>(argument: T)
    where T: Add,
          T: Incrementable
{
    argument + T::incrementor()
}

通用的结构和枚举 (Generic Structs and Enums)

Just like functions, structs and enums can be generic. Let's take a look at the standard library version of Option<T>:

就像函数一样,结构体和枚举也可以是通用的。让我们来看看标准库中的Option<T>

enum Option<T> {
    Some: T,
    None: (),
}

Just like an unconstrained generic function, this type exists for all (∀) types T. Result<T, E> is another example:

就像无约束泛型函数一样,这种类型存在于所有(∀)类型TResult<T, E>是另一个例子:

enum Result<T, E> {
    Ok: T,
    Err: E,
}

Both generic enums and generic structs can be trait constrained, as well. Consider this struct:

泛型枚举和泛型结构也都可以被特质约束。考虑一下这个结构:

struct Foo<T>
    where T: Add
{
    field_one: T,
}

类型参数 (Type Arguments)

Similar to Rust, Sway has what is colloquially known as the turbofish. The turbofish looks like this: ::<> (see the little fish with bubbles behind it?). The turbofish is used to annotate types in a generic context. Say you have the following function:

与 Rust 类似,Sway 具有俗称的 [turbofish](https://github.com/rust-lang/rust/blob/e98309298d927307c5184f4869604bd068d26183/src/test/ui/parser/bastion-of-the-turbofish。 rs).涡轮鱼看起来像这样:::<>(看到后面有气泡的小鱼了吗?)。 turbofish 用于在通用上下文中注释类型。假设您具有以下功能:

fn foo<T, E>(t: T) -> Result<T, E> {
    Result::Ok(t)
}

In this code example, which is admittedly asinine, you can't possibly know what type E is. You'd need to provide the type manually, with a turbofish:

在这个公认是愚蠢的代码示例中,您不可能知道E是什么类型。您需要使用涡轮鱼手动提供类型:

fn foo<T, E>(t: T) -> Result<T, E> {
    Result::Ok::<T, MyErrorType>(t)
}

It is also common to see the turbofish used on the function itself:

在函数本身上使用 turbofish 也很常见:

fn main() {
    foo::<Bar, Baz>()
}

Last updated