英文:
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)
}
但是,如果U
与item
的实际类型不兼容,该代码将会导致恐慌(panic):
以下代码将会导致恐慌:
fmt.Println(Transform[string, int]("foo"))
// 输出:panic: interface conversion: interface {} is string, not int
以下代码不会导致恐慌,因为bytes.Buffer
同时实现了io.Reader
和io.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
。
如果你真的想要返回**T
或U
中的任意一个**,你需要将返回类型声明为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)
}
你必须使用断言,因为类型参数T
和U
都受到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'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("transform function not set")
return
}
u = s.transform(elem)
return
}
Playground: https://go.dev/play/p/GKLRmKKWxlP
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论