Есть ли способ сделать что-то похожее на приведенное ниже сигнатуру данного типа?
func Transform[T, U any](item T) U {
return item
}
Приведенный выше код дает следующую ошибку:
cannot use item (variable of type T constrained by any) as U value in return
statement
Я не могу использовать приведенную выше сигнатуру типа, так как я, по сути, пытаюсь создать необязательный метод преобразования, который иногда нужно будет преобразовать из 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, которая иногда условно просто возвращает себя?
Я пытаюсь написать код, который принимает функцию преобразования, но функция преобразования не является обязательной. В случае, если он не указан, я хочу иметь возможность использовать исходное значение T, но столкнулся с проблемами, когда компилятор требует, чтобы я преобразовал его в U
Вы можете заставить свой фрагмент кода работать:
func Transform[T, U any](item T) U {
return any(item).(U)
}
Но код паникует, если U
не совместим с утверждениями с фактическим типом item
:
Это вызовет панику:
fmt.Println(Transform[string, int]("foo"))
// Output: panic: interface conversion: interface {} is string, not int
Это удается без паники, потому что bytes.Buffer
реализует интерфейс io.Reader
и io.Writer
:
b := &bytes.Buffer{}
_ = Transform[io.Writer, io.Reader](b)
Итак, можно делать то, что вы хотите. Но я не уверен, насколько это полезно, так как сбой во время выполнения фактического аргумента несовместим с U
.
Это в сочетании с некоторым отражением решило мой вариант использования. Отражение используется, чтобы поймать случай паники и вместо этого вернуть ошибку.
Вы не можете вернуть T
, если ваша функция определена как возвращающая U
. Даже если вы утверждаете T
на U
, функция все равно возвращает U
.
Если вы действительно хотите вернуть именно T
или U
, вам нужно объявить возвращаемый тип как any
:
func (s SomeStruct[T, U]) Transform(elem T) any { /* ... */ }
... который отказывается от статической типизации; или вы можете использовать вспомогательный тип "любой":
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)}
}
Посмотрите, как использовать его на этой игровой площадке.
Альтернативы будут отклоняться от заявленной вами цели. Один из них — фактически утвердить T
для U
, хотя вы должны знать, что это возвращает U
, а не T
, даже если значение T
присваивается 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)
}
Вы должны использовать утверждение, потому что оба параметра типа T
и U
ограничены any
, поэтому (правильно) нет другого способа выразить конвертируемость между ними. Идиома «запятая-ок» помогает избежать паники во время выполнения.
Или вы можете вместо этого вернуть (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
}
Детская площадка: https://go.dev/play/p/GKLRmKKWxlP
Нет, вы не можете создать функцию с различными типами возвращаемых значений (или типами аргументов, если уж на то пошло). Вы можете заставить его возвращать тип интерфейса, но это все.