Go 1.22 的新增功能系列之二:reflect.TypeFor

GoCN

共 2494字,需浏览 5分钟

 · 2024-04-28

Go 1.22 的第一个候选版本已经发布,这意味着最终版本即将发布,现在是我在博客中介绍我在这个周期中所做工作的时候了。像往常一样,我的贡献很小,但它们是我的,所以我将从幕后的角度来谈谈它们。首先是reflect.TypeFor。

这是整个函数:

// TypeFor returns the [Type] that represents the type argument T.func TypeFor[T any]() Type {  return TypeOf((*T)(nil)).Elem()}

很简单!测试代码比实际函数本身长大约十倍。

当您想要为某种类型创建 reflect.Type 对象时,例如 var intType = reflect.TypeFor[int]() 或 var errorType = reflect.TypeFor[error]() ,请使用此函数。

将其添加到反射包中的提案由 Josh Bleecher Snyder 提出。它标志着泛型通过标准库的不断进步又迈出了一步。

该提案最困难的部分可能是为该函数找到正确的名称。 reflect.TypeOf 将是该函数最自然的名称,但它已被现有函数用于创建 reflect.Type 对象。考虑的替代方案包括:

  • StaticTypeOf

  • MakeType

  • TypeOfT

  • TypeFrom

  • GetType

  • AsType

  • ToType

  • NewType

  • TheType

  • Types

  • ConstantType

  • T

命名事物仍然是计算机科学中的两个难题之一。

我扔掉了获胜的名字,但那时我们只是浏览所有可能的形容词和介词的列表,所以最终有人会选择 TypeFor 。

总的来说,我反对“无用地使用泛型”,即使用泛型函数,即使它没有添加任何类型安全性并且没有或只有最小的长度节省。例如,制作 json.Marshal 的泛型版本对泛型来说是无用的,因为它实际上并没有添加任何类型安全性。然而,在这种情况下,为接口构建 reflect.Type 的正确方法相当模糊,所以我认为值得添加。

这个问题可以追溯到 Go 中的反射第一定律:“反射从接口值到反射对象。”当您调用 reflect.TypeOf(0) 时,您会得到您所期望的:类型 int 的 reflect.Type 对象。但如果您致电 reflect.TypeOf(err) ,您可能会对所得到的结果感到惊讶。您不会获取包含类型 error 的对象,而是获取具有底层具体类型 err 的对象。这是因为 error 是一个接口值,而 Go 中调用函数时,函数的接口参数会根据需要进行转换,所以错误接口丢失,只有具体值对 reflect.TypeOf 。更糟糕的是,如果 err 为零,您会从 reflect.TypeOf 返回零。

Chris Siebenmann 对正在发生的事情以及如何解决它做了很好的解释:

因为 reflect.TypeOf() 传递了一个“ interface{} ”(现在也称为“ any ”),所以它不能直接给你一个reflect.Type接口,因为该接口类型在转换为“ interface{} ”时将会丢失。相反, reflect.TypeOf() 返回底层非接口类型(或 nil)。正如我们在“ nil ”只是某种类型的上下文中所看到的,要解决这个问题,您必须传递一个指向接口的指针(好吧,接口的值),返回类型“指向”的指针,然后再次通过反射取消引用原始类型......

在 Go 1.22 之前,如果要为接口创建 reflect.Type ,解决方案是调用 reflect.TypeOf((*TheInterface)(nil)).Elem() 。这可行,但不是很直观。我知道我很难在我自己的使用反射包的代码中弄清楚它。

当 reflect.TypeFor 被提出时,还有一个替代提案,它也添加了reflect.ValueOf函数的通用版本。我在问题评论中认为这是一个错误:

粗略地看一下标准库,就会发现有许多地方可以用 reflect.T[whatever]() 来替换,以便更加清晰。reflect.GenericValueOf 更难限定,因为在某些地方接口类型显然是我们想要的,但在很多情况下,底层的具体类型才是目标。添加 reflect.GenericValueOf 会在阅读代码时增加一定程度的歧义:作者是否真的想要这里的接口类型,还是他们只是看到泛型并假设该函数更新,因此更好?它添加了第二种做事方式,但并没有 100% 清楚其意图。所以我倾向于在 reflect.T 上+1,但在 reflect.GenericValueOf 上+0。

有时,Go 标准库最好的东西就是所有被遗漏的东西。在我看来, reflect.GenericValueOf 会越过模糊线,变成“无用的泛型使用”。

这就是 reflect.TypeFor 的故事。标准库中的每个函数背后都有一个故事。

浏览 242
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报