将类型T转换为类型U的身份函数中。

huangapple go评论113阅读模式
英文:

Convert type T to type U in an identity function

问题

根据给定的类型签名,是否有办法实现类似下面的操作?

  1. func Transform[T, U any](item T) U {
  2. return item
  3. }

上述代码会产生以下错误:

  1. 无法将类型为 T(受 any 约束的变量)用作返回语句中的 U

我无法使用上述类型签名,因为我实际上想要创建一个可选的转换方法,有时需要将 T 转换为 U,但有时只需返回自身。下面是一个更详细的用例示例。

  1. type SomeStruct[T, U any] struct {
  2. Transform func(T) U
  3. }
  4. func (s SomeStruct[T, U]) Transform(elem T) U {
  5. if s.Transform != nil {
  6. return s.Transform(elem)
  7. }
  8. return elem
  9. }

是否有办法创建一个条件性地返回自身的 Transform 函数?

英文:

With the given type signature below, is there a way to do something similar to below?

  1. func Transform[T, U any](item T) U {
  2. return item
  3. }

The code above gives the following error:

  1. cannot use item (variable of type T constrained by any) as U value in return
  2. statement

I am unable to use the type signature above, as I am essentially trying to make an optional transform method that sometimes will need to convert from T to U, but sometimes just return itself. A more detailed example of the use case is shown below.

  1. type SomeStruct[T, U any] struct {
  2. Transform func(T) U
  3. }
  4. func (s SomeStruct[T, U]) Transform(elem T) (U) {
  5. if s.Transform != nil {
  6. return s.Transform(elem)
  7. }
  8. return elem
  9. }

Is there a way to create a Transform function that sometimes conditionally just returns itself?

答案1

得分: 3

你可以使你的代码片段工作:

  1. func Transform[T, U any](item T) U {
  2. return any(item).(U)
  3. }

但是,如果Uitem的实际类型不兼容,该代码将会导致恐慌(panic):

以下代码将会导致恐慌:

  1. fmt.Println(Transform[string, int]("foo"))
  2. // 输出:panic: interface conversion: interface {} is string, not int

以下代码不会导致恐慌,因为bytes.Buffer同时实现了io.Readerio.Writer接口:

  1. b := &bytes.Buffer{}
  2. _ = Transform[io.Writer, io.Reader](b)

所以,你可以实现你想要的功能。但是我不确定它有多有用,因为如果实际参数与U不兼容,它会在运行时失败。

英文:

You can make your code snippet work:

  1. func Transform[T, U any](item T) U {
  2. return any(item).(U)
  3. }

But the code will fail with a panic if U is not assertion-compatible with the actual type of item:

This will panic:

  1. fmt.Println(Transform[string, int]("foo"))
  2. // Output: panic: interface conversion: interface {} is string, not int

This succeeds without panic, because bytes.Buffer is implements both the io.Reader and io.Writer interface:

  1. b := &bytes.Buffer{}
  2. _ = Transform[io.Writer, io.Reader](b)

So, it's possible to do what you want. But I'm not sure how useful it is, as fails at runtime of the actual argument isn't compatible with U.

答案2

得分: 1

你的函数定义为返回U,那么就不能返回T。即使你将T断言为U,函数仍然返回U

如果你真的想要返回**TU中的任意一个**,你需要将返回类型声明为any

  1. func (s SomeStruct[T, U]) Transform(elem T) any { /* ... */ }

这样做会放弃静态类型检查;或者你可以使用一个辅助的"either"类型:

  1. type Either[T, U any] struct {
  2. value any
  3. }
  4. // Either的方法
  5. func (s SomeStruct[T, U]) Transform(elem T) Either[T, U] {
  6. if s.transform == nil {
  7. return Either[T, U]{elem}
  8. }
  9. return Either[T, U]{s.transform(elem)}
  10. }

这个playground中可以看到如何使用它。


其他的方法会偏离你所说的目标。其中一种方法是将T断言为U,尽管你必须意识到,即使T的值可以赋值给U,这也会返回U而不是T

  1. func (s SomeStruct[T, U]) Transform(elem T) U {
  2. if s.transform == nil {
  3. if u, ok := any(elem).(U); ok {
  4. return u
  5. }
  6. return *new(U) // 如果断言失败,返回U的零值
  7. }
  8. return s.transform(elem)
  9. }

你必须使用断言,因为类型参数TU都受到any的约束,所以没有其他方法可以表达两者之间的可转换性(convertibility)。逗号-OK惯用法有助于避免运行时panic。

或者你可以返回(U, error)这通常是合理的做法

  1. func (s SomeStruct[T, U]) Transform(elem T) (u U, err error) {
  2. if s.transform == nil {
  3. err = errors.New("transform function not set")
  4. return
  5. }
  6. u = s.transform(elem)
  7. return
  8. }

Playground: https://go.dev/play/p/GKLRmKKWxlP

英文:

You can't return T if your function is defined to return U. Even if you assert T to U, the function is still returning U.

If you really want to return exactly either T or U, you need to declare the return type as any:

  1. func (s SomeStruct[T, U]) Transform(elem T) any { /* ... */ }

...which gives up static typing; or you could use a helper "either" type:

  1. type Either[T, U any] struct {
  2. value any
  3. }
  4. // Either methods
  5. func (s SomeStruct[T, U]) Transform(elem T) Either[T, U] {
  6. if s.transform == nil {
  7. return Either[T, U]{elem}
  8. }
  9. return Either[T, U]{s.transform(elem)}
  10. }

See how to use it in this playground.

<hr>

Alternatives will deviate from your stated goal. One is to actually assert T to U, though you must be aware that this returns U, not T, even if T's value is assignable to U:

  1. func (s SomeStruct[T, U]) Transform(elem T) U {
  2. if s.transform == nil {
  3. if u, ok := any(elem).(U); ok {
  4. return u
  5. }
  6. return *new(U) // U&#39;s zero value if elem assertion fails
  7. }
  8. return s.transform(elem)
  9. }

You must use an assertion because both type parameters T and U are constrained by any, so there's (rightfully) no other way to express convertibility between the two. The comma-ok idiom helps avoid run-time panics.

Or you can return (U, error) instead, which is, generally, the reasonable thing to do:

  1. func (s SomeStruct[T, U]) Transform(elem T) (u U, err error) {
  2. if s.transform == nil {
  3. err = errors.New(&quot;transform function not set&quot;)
  4. return
  5. }
  6. u = s.transform(elem)
  7. return
  8. }

Playground: https://go.dev/play/p/GKLRmKKWxlP

huangapple
  • 本文由 发表于 2022年11月11日 07:39:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/74396517.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定