Я пытаюсь понять реализацию дженериков в go1.18. В моем тестовом примере я сохраняю серию тестов и пытаюсь вызвать функциональную переменную. К сожалению, я получаю сообщение об ошибке в функции EvalCases, когда пытаюсь использовать переменную tc.input, я получаю следующую ошибку:
cannot use input (variable of type T constrained by Inputer) as type string in argument to fn
Почему я получаю эту ошибку и как ее исправить?
import (
"fmt"
"strconv"
)
type BoolCase func(string) bool
type Inputer interface {
int | float64 | ~string
}
type Wanter interface {
Inputer | bool
}
type TestCase[T Inputer, U Wanter] struct {
input T
want U
}
type TestConditions[T Inputer, U Wanter] map[string]TestCase[T, U]
// IsNumeric validates that a string is either a valid int64 or float64
func IsNumeric(s string) bool {
_, err := strconv.ParseFloat(s, 64)
return err == nil
}
func EvalCases[T Inputer, U Wanter](cases TestConditions[T, U], fn BoolCase) {
for name, tc := range cases {
input := T(tc.input)
want := tc.want
// Error: cannot use input (variable of type T constrained by Inputer) as type string in argument to fn
got := fn(input)
fmt.Printf("name: %-20s | input: %-10v | want: %-10v | got: %v\n", name, input, want, got)
}
}
func main() {
var cases = TestConditions[string, bool]{
"empty": {input: "", want: false},
"integer": {input: "123", want: true},
"float": {input: "123.456", want: true},
}
fn := IsNumeric
EvalCases(cases, fn)
}
Why am I receiving that error
Поскольку fn
— это BoolFunc
, который является func(string) bool
, поэтому в качестве параметра требуется string
, но input
имеет тип T
. Кроме того, согласно вашему определению, T
удовлетворяет ограничению Inputer
и, таким образом, может также принимать тип int
, float64
или любой другой не-string
тип, который имеет string
в качестве базового типа (~string
), ни один из которых неявно не приводится к string
.
how do I fix it?
Вам нужно изменить определение BoolCase
, чтобы оно имело параметр общего типа для одного параметра. Вы можете ограничить его Inputer
, но вы также можете использовать any
(interface{}
).
type BoolCase[T any] func(T) bool
Затем обязательно укажите этот параметр универсального типа в сигнатуре функции EvalCases
:
func EvalCases[T Inputer, U Wanter](cases TestConditions[T, U], fn BoolCase[T])
https://go.dev/play/p/RdjQXJ0WpDh
@Zyl, я предполагал, что реализация тип BoolCase[T любой] func(T) bool приведет к тому, что мне придется реструктурировать функцию числовой? Почему бы мне не сделать это, чтобы удовлетворить интерфейс?
Как будет выглядеть реструктурированная функция? string
удовлетворяет Inputer
; это только обратное, что не обязательно верно. Обычно типизированные значения будут принимать конкретные типы во время выполнения, но во время компиляции ваши T
и U
не будут обрабатываться по-разному. Обратите внимание, что не все языки делают это так. C++, например, требует выяснить все потенциальные типы T
во время компиляции, чтобы затем выяснить, может ли код работать. Например. код, использующий оператор +
для значений T
, обычно компилируется нормально, если вы не передаете нечисловой тип. Попробуйте поиграть с reflect.TypeOf()
.
@zyl Спасибо за объяснение.
string