doc/faq: expand discussion of generic methods
Rob Pike pointed out that it could be clearer and more self-contained, as well as being clear about generic methods never happening. Change-Id: I854a38e20b4a7cd695bb3fa0a237759c5f7224d3 Reviewed-on: https://go-review.googlesource.com/c/website/+/599475 Auto-Submit: Russ Cox <rsc@golang.org> Reviewed-by: Ian Lance Taylor <iant@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Родитель
ed6d739b5c
Коммит
388f3d03b7
|
@ -1669,18 +1669,93 @@ code.
|
|||
Go permits a generic type to have methods, but, other than the
|
||||
receiver, the arguments to those methods cannot use parameterized
|
||||
types.
|
||||
The methods of a type determines the interfaces that the type
|
||||
implements, but it is not clear how this would work with parameterized
|
||||
arguments for methods of generic types.
|
||||
It would require either instantiating functions at run time or
|
||||
instantiating every generic function for every possible type
|
||||
argument.
|
||||
Neither approach seems feasible.
|
||||
For more details, including an example, see the
|
||||
[proposal](/design/43651-type-parameters#no-parameterized-methods).
|
||||
We do not anticipate that Go will ever add generic methods.
|
||||
|
||||
The problem is how to implement them.
|
||||
Specifically, consider checking whether a value in an
|
||||
interface implements another interface with additional methods.
|
||||
For example, consider this type, an empty struct with a
|
||||
generic `Nop` method that returns its argument, for any possible type:
|
||||
|
||||
```
|
||||
type Empty struct{}
|
||||
|
||||
func (Empty) Nop[T any](x T) T {
|
||||
return x
|
||||
}
|
||||
```
|
||||
|
||||
Now suppose an `Empty` value is stored in an `any` and passed
|
||||
to other code that checks what it can do:
|
||||
|
||||
```
|
||||
func TryNops(x any) {
|
||||
if x, ok := x.(interface{ Nop(string) string }); ok {
|
||||
fmt.Printf("string %s\n", x.Nop("hello"))
|
||||
}
|
||||
if x, ok := x.(interface{ Nop(int) int }); ok {
|
||||
fmt.Printf("int %d\n", x.Nop(42))
|
||||
}
|
||||
if x, ok := x.(interface{ Nop(io.Reader) io.Reader }); ok {
|
||||
data, err := io.ReadAll(x.Nop(strings.NewReader("hello world")))
|
||||
fmt.Printf("reader %q %v\n", data, err)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
How does that code work if `x` is an `Empty`?
|
||||
It seems that `x` must satisfy all three tests,
|
||||
along with any other form with any other type.
|
||||
|
||||
What code runs when those methods are called?
|
||||
For non-generic methods, the compiler generates the code
|
||||
for all method implementations and links them into the final program.
|
||||
But for generic methods, there can be an infinite number of method
|
||||
implementations, so a different strategy is needed.
|
||||
|
||||
There are four choices:
|
||||
|
||||
1. At link time, make a list of all the possible dynamic interface checks,
|
||||
and then look for types that would satisfy them but are missing
|
||||
compiled methods, and then reinvoke the compiler to add those methods.
|
||||
|
||||
This would make builds significantly slower, by needing to stop after
|
||||
linking and repeat some compilations. It would especially slow down
|
||||
incremental builds. Worse, it is possible that the newly compiled method
|
||||
code would itself have new dynamic interface checks, and the process
|
||||
would have to be repeated. Examples can be constructed where
|
||||
the process never even finishes.
|
||||
|
||||
2. Implement some kind of JIT, compiling the needed method code at runtime.
|
||||
|
||||
Go benefits greatly from the simplicity and predictable performance
|
||||
of being purely ahead-of-time compiled.
|
||||
We are reluctant to take on the complexity of a JIT just to implement
|
||||
one language feature.
|
||||
|
||||
3. Arrange to emit a slow fallback for each generic method that uses
|
||||
a table of functions for every possible language operation on the type parameter,
|
||||
and then use that fallback implementation for the dynamic tests.
|
||||
|
||||
This approach would make a generic method parameterized by an
|
||||
unexpected type much slower than the same method
|
||||
parameterized by a type observed at compile time.
|
||||
This would make performance much less predictable.
|
||||
|
||||
4. Define that generic methods cannot be used to satisfy interfaces at all.
|
||||
|
||||
Interfaces are an essential part of programming in Go.
|
||||
Disallowing generic methods from satisfying interfaces is unacceptable
|
||||
from a design point of view.
|
||||
|
||||
None of these choices are good ones, so we chose “none of the above.”
|
||||
|
||||
Instead of methods with type parameters, use top-level functions with
|
||||
type parameters, or add the type parameters to the receiver type.
|
||||
|
||||
For more details, including more examples, see the
|
||||
[proposal](/design/43651-type-parameters#no-parameterized-methods).
|
||||
|
||||
### Why can't I use a more specific type for the receiver of a parameterized type? {#types_in_method_declaration}
|
||||
|
||||
The method declarations of a generic type are written with a receiver
|
||||
|
|
Загрузка…
Ссылка в новой задаче