Преобразование типа T в тип U в функции идентификации

Есть ли способ сделать что-то похожее на приведенное ниже сигнатуру данного типа?

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, которая иногда условно просто возвращает себя?

Нет, вы не можете создать функцию с различными типами возвращаемых значений (или типами аргументов, если уж на то пошло). Вы можете заставить его возвращать тип интерфейса, но это все.

Peter 11.11.2022 01:10

Я пытаюсь написать код, который принимает функцию преобразования, но функция преобразования не является обязательной. В случае, если он не указан, я хочу иметь возможность использовать исходное значение T, но столкнулся с проблемами, когда компилятор требует, чтобы я преобразовал его в U

nimda 12.11.2022 07:28
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
Создание API ввода вопросов на разных языках программирования (Python, PHP, Go и Node.js)
API ввода вопросов - это полезный инструмент для интеграции моделей машинного обучения, таких как ChatGPT, в приложения, требующие обработки...
3
2
68
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Вы можете заставить свой фрагмент кода работать:

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.

Это в сочетании с некоторым отражением решило мой вариант использования. Отражение используется, чтобы поймать случай паники и вместо этого вернуть ошибку.

nimda 17.11.2022 00:12

Вы не можете вернуть 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

Другие вопросы по теме