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

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

Convert type T to type U in an identity function

问题

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

func Transform[T, U any](item T) U {
    return item
}

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

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

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

type SomeStruct[T, U any] struct {
    Transform func(T) U
}

func (s SomeStruct[T, U]) Transform(elem T) U {
    if s.Transform != nil {
        return s.Transform(elem)
    }
    return elem
}

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

英文:

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

func Transform[T, U any](item T) U {
	return item
}

The code above gives the following error:

cannot use item (variable of type T constrained by any) as U value in return 
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.

type SomeStruct[T, U any] struct {
	Transform func(T) U
}

func (s SomeStruct[T, U]) Transform(elem T) (U) {
	if s.Transform != nil {
		return s.Transform(elem)
	}
	return elem
}

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

答案1

得分: 3

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

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

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

以下代码将会导致恐慌:

fmt.Println(Transform[string, int]("foo"))

// 输出:panic: interface conversion: interface {} is string, not int

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

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

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

英文:

You can make your code snippet work:

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

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

This will panic:

fmt.Println(Transform[string, int]("foo"))

// 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:

b := &bytes.Buffer{}
_ = 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

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

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

type Either[T, U any] struct {
	value any
}

// Either的方法

func (s SomeStruct[T, U]) Transform(elem T) Either[T, U] {
	if s.transform == nil {
		return Either[T, U]{elem}
	}
	return Either[T, U]{s.transform(elem)}
}

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


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

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

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

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

func (s SomeStruct[T, U]) Transform(elem T) (u U, err error) {
	if s.transform == nil {
        err = errors.New("transform function not set")
		return 
	}
	u = s.transform(elem)
    return
}

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:

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

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

type Either[T, U any] struct {
	value any
}

// Either methods

func (s SomeStruct[T, U]) Transform(elem T) Either[T, U] {
	if s.transform == nil {
		return Either[T, U]{elem}
	}
	return Either[T, U]{s.transform(elem)}
}

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:

func (s SomeStruct[T, U]) Transform(elem T) U {
	if s.transform == nil {
		if u, ok := any(elem).(U); ok {
			return u
		}
		return *new(U) // U&#39;s zero value if elem assertion fails
	}
	return s.transform(elem)
}

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:

func (s SomeStruct[T, U]) Transform(elem T) (u U, err error) {
	if s.transform == nil {
        err = errors.New(&quot;transform function not set&quot;)
		return 
	}
	u = s.transform(elem)
    return
}

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:

确定