我正在通過修改我為處理切片而創建的庫來玩轉泛型。我有一個Difference函式,它接受切片并回傳僅在其中一個切片中找到的唯一元素串列。
我修改了函式以使用泛型,我正在嘗試使用不同型別(例如字串和整數)撰寫單元測驗,但在使用聯合型別時遇到了問題。這是我現在擁有的:
type testDifferenceInput[T comparable] [][]T
type testDifferenceOutput[T comparable] []T
type testDifference[T comparable] struct {
input testDifferenceInput[T]
output testDifferenceOutput[T]
}
func TestDifference(t *testing.T) {
for i, tt := range []testDifference[int] {
testDifference[int]{
input: testDifferenceInput[int]{
[]int{1, 2, 3, 3, 4},
[]int{1, 2, 5},
[]int{1, 3, 6},
},
output: []int{4, 5, 6},
},
} {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
actual := Difference(tt.input...)
if !isEqual(actual, tt.output) {
t.Errorf("expected: %v %T, received: %v %T", tt.output, tt.output, actual, actual)
}
})
}
}
我希望能夠在同一個表測驗中測驗 int 或 string。這是我嘗試過的:
type intOrString interface {
int | string
}
type testDifferenceInput[T comparable] [][]T
type testDifferenceOutput[T comparable] []T
type testDifference[T comparable] struct {
input testDifferenceInput[T]
output testDifferenceOutput[T]
}
func TestDifference(t *testing.T) {
for i, tt := range []testDifference[intOrString] {
testDifference[int]{
input: testDifferenceInput[int]{
[]int{1, 2, 3, 3, 4},
[]int{1, 2, 5},
[]int{1, 3, 6},
},
output: []int{4, 5, 6},
},
testDifference[string]{
input: testDifferenceInput[string]{
[]string{"1", "2", "3", "3", "4"},
[]string{"1", "2", "5"},
[]string{"1", "3", "6"},
},
output: []string{"4", "5", "6"},
},
} {
t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
actual := Difference(tt.input...)
if !isEqual(actual, tt.output) {
t.Errorf("expected: %v %T, received: %v %T", tt.output, tt.output, actual, actual)
}
})
}
}
但是,在運行它時,我收到以下錯誤:
$ go version
go version dev.go2go-55626ee50b linux/amd64
$ go tool go2go test
arrayOperations_unit_test.go2:142:6: expected ';', found '|' (and 5 more errors)
為什么它抱怨我的intOrString界面?
編輯#1 - 在@Nulo 的幫助下,我可以確認gotip確實有效,而且我現在明白為什么我不能intOrString用作型別 - 它應該是一個約束。
但是,在我的表測驗中找到一些混合整數和字串的方法仍然會很好......
$ gotip version
go version devel go1.18-c812b97 Fri Oct 29 22:29:31 2021 0000 linux/amd64
$ gotip test
# github.com/adam-hanna/arrayOperations/go2 [github.com/adam-hanna/arrayOperations/go2.test]
./arrayOperations_unit_test.go:152:39: interface contains type constraints
./arrayOperations_unit_test.go:152:39: intOrString does not satisfy intOrString
./arrayOperations_unit_test.go:155:6: incompatible type: cannot use []int{…} (value of type []int) as []intOrString value
./arrayOperations_unit_test.go:156:6: incompatible type: cannot use []int{…} (value of type []int) as []intOrString value
./arrayOperations_unit_test.go:157:6: incompatible type: cannot use []int{…} (value of type []int) as []intOrString value
./arrayOperations_unit_test.go:159:13: incompatible type: cannot use []int{…} (value of type []int) as testDifferenceOutput[intOrString] value
./arrayOperations_unit_test.go:163:6: incompatible type: cannot use []string{…} (value of type []string) as []intOrString value
./arrayOperations_unit_test.go:164:6: incompatible type: cannot use []string{…} (value of type []string) as []intOrString value
./arrayOperations_unit_test.go:165:6: incompatible type: cannot use []string{…} (value of type []string) as []intOrString value
./arrayOperations_unit_test.go:167:13: incompatible type: cannot use []string{…} (value of type []string) as testDifferenceOutput[intOrString] value
./arrayOperations_unit_test.go:152:39: too many errors
FAIL github.com/adam-hanna/arrayOperations/go2 [build failed]
uj5u.com熱心網友回復:
我已經通過gotip傳遞了您的代碼,該代碼使用了提案的更進化的實作,并且它沒有抱怨代碼的那部分,所以我認為問題出在 go2go 的初始實作上。
請注意,您的實作將不起作用,因為您絕對可以在型別斷言運算式中使用引數介面,但是您不能像在其中那樣使用帶有型別串列的介面 testDifference[intOrString]
uj5u.com熱心網友回復:
Go2 Playground 和go2go命令已經過時并被明確拋在后面,因為 Go 團隊正在專注于實際實作。當go2go不起作用時,您必須使用gotip.
使用intOrString作為一種型別
您不能這樣做,因為通過包含型別集,它是一個介面約束,并且明確不支持將其用作型別。允許約束作為普通介面型別:
這是我們現在不建議的功能,但可以考慮用于該語言的更高版本。
所以首先要做的是intOrString用作實際約束,因此在型別引數串列中。下面我替換comparable為intOrString:
type testDifferenceInput[T intOrString] [][]T
type testDifferenceOutput[T intOrString] []T
type testDifference[T intOrString] struct {
input testDifferenceInput[T]
output testDifferenceOutput[T]
}
話雖如此,您明白為什么不能使用約束將具體型別實體化為您的測驗切片,如[]testDifference[intOrString].
構建測驗切片
您遇到的第二個問題是測驗切片包含兩個不相關型別的結構。一個是testDifference[int],一個是testDifference[string]。即使型別testDifference本身是引數化的,它的具體實體化也不是同一個型別。為了更清楚地說明這一點,請考慮沒有型別引數的情況:
// bad
for i, tt := range [] /* what type to use here?? ?*/ {
[]int{1,2,3},
[]string{"a","b","c"},
}
上面即使使用型別引數也失敗的原因與我在這里解釋的相似。如果您需要一個包含不同型別的切片,您唯一的選擇是[]interface{}.
...或者,您只需將切片分開:
ttInts := []testDifference[int]{
testDifference[int]{
input: testDifferenceInput[int]{
[]int{1, 2, 3, 3, 4},
[]int{1, 2, 5},
[]int{1, 3, 6},
},
output: []int{4, 5, 6},
},
}
ttStrs := testDifference[string]{ /* etc. */ }
帶有int | string型別集的約束
這在技術上是可行的,但您必須記住,具有型別集的泛型型別允許型別集中所有型別支持的操作。基于型別集的操作:
規則是泛型函式可以以引數約束的型別集的每個成員允許的任何方式使用型別為型別引數的值。
那么int和上允許的操作是什么string?簡而言之:
- var 宣告 (
var foo T) - 轉換和斷言
T(x)以及x.(T) - 比較 (
==,!=) - 排序 (
<,<=,>和>=) - 該
運營商
所以你可以有一個intOrString約束,但使用它的函式Difference,包括你的 func ,僅限于這些操作。例如:
type intOrString interface {
int | string
}
func beforeIntOrString[T intOrString](a, b T) bool {
return a < b
}
func sumIntOrString[T intOrString](a, b T) T {
return a b
}
func main() {
fmt.Println(beforeIntOrString("foo", "bar")) // false
fmt.Println(beforeIntOrString(4, 5)) // true
fmt.Println(sumIntOrString("foo", "bar")) // foobar
fmt.Println(sumIntOrString(10, 5)) // 15
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/qukuanlian/342898.html
