New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
proposal: Go 2: remove interface{} use from the standard library #23077
Comments
|
Here’s an overview of the use of interface{} in each standard library case. Some of these implementations are complex and my interpretation may be wrong or there may not be enough detail here.
Constructs a panic string, expects types that can be converted to string.
Returns the item passed into panic.
A container/heap client constructs a type that adheres to heap.Interface and these heap package functions call heap.Interface methods. The provided type may define a non-interface{} type contained but will have to do a type assertion on calling heap.Pop if so.
Unlike container/heap, container/list provides the type constructed of list.Element structs. Each Element has a “Value interface{}” field where the reference to the client’s item is stored. Remove requires a type assertion for non-interface{} types referenced by Value.
container/ring follows the same pattern as container/list by providing a container type where each Ring (item) in the complete Ring has a “Value interface{}” field. Do will execute the provided function on each Ring in the Ring, in which a type assertion is required for non-interface{} item types. Clients access elements by directly referencing Value in the struct which also requires a type assertion.
The provided parent context has a data store accessible by the interface method “Value(key interface{}) interface{}”. This function copies the parent context and sets the key to val. “Packages that define a Context key should provide type-safe accessors for the values stored using that key”
“The parameter pub is the public key of the signee and priv is the private key of the signer…All keys types that are implemented via crypto.Signer are supported (This includes *rsa.PublicKey and *ecdsa.PublicKey.)” crypto defines a Signer interface that works with named types PublicKey, PrivateKey represented by interface{}, I’m not sure why those crypto types aren’t already being used in crypto/x509.
The provided arguments are passed to the driver as a processed slice of interface{}.
Values returned from the driver are converted from string, []byte, time.Time, or nil into similar outputs or through a reflect-driven conversion to friendly numeric types.
The database driver provides ValueConverter types or uses database/sql/driver ValueConverters that convert Go types (passed in as interface{}) into Value (an interface{}) that represents a Value the database can handle (nil, int64, float64, bool, []byte, string, or time.Time).
Uses reflect with struct tags to compile a []byte from an input interface{} that adheres to possible encodable structures (or an error is returned).
The struct provided by interface{} that’s filled in by the function must use upper case fields.
The output data interface{} can be a pointer to any bool or integer type or a slice of any bool or integer types.
Returns the size of the input interface{} for slices, arrays, structs, or any numeric type, otherwise it returns -1.
Write the binary encoding of the input into the provided io.Writer. First a type switch is tried for integer typed slice, integer typed pointer, or direct integer types, otherwise a reflect-based encoding is tried.
Registers the base type of the input during initialization. RegisterName uses a provided string for the type name.
“Decode reads the next value from the input stream and stores it in the data represented by the empty interface value. If e is nil, the value will be discarded. Otherwise, the value underlying e must be a pointer to the correct type for the next data item received.”
“Encode transmits the data item represented by the empty interface value, guaranteeing that all necessary type information has been transmitted first. Passing a nil pointer to Encoder will panic, as they cannot be transmitted by gob.”
“Func implements Var by calling the function and formatting the returned value using JSON.”
A type switch is used before reflection to parse depending on the underlying type of each input interface{} associated with the format verb (%v, %d).
Writes the type of the provided item.
“Node formats node in canonical gofmt style and writes the result to dst. The node type must be *ast.File, *printer.CommentedNode, []ast.Decl, []ast.Stmt, or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt. Node does not modify node. Imports are not sorted for nodes representing partial source files (i.e., if the node is not an *ast.File or a *printer.CommentedNode not wrapping an *ast.File).”
“ParseExprFrom is a convenience function for parsing an expression. The arguments have the same meaning as for ParseFile, but the source must be a valid Go (type or value) expression. Specifically, fset must not be nil.”
“If src != nil, ParseFile parses the source from src and the filename is only used when recording position information. The type of the argument for the src parameter must be string, []byte, or io.Reader. If src == nil, ParseFile parses the file specified by filename.”
“Fprint "pretty-prints" an AST node to output for a given configuration cfg. Position information is interpreted relative to the file set fset. The node type must be *ast.File, *CommentedNode, []ast.Decl, []ast.Stmt, or assignment-compatible to ast.Expr, ast.Decl, ast.Spec, or ast.Stmt.”
“evalArgs formats the list of arguments into a string. It is therefore equivalent to fmt.Sprint(args...) except that each argument is indirected (if a pointer), as required, using the same rules as the default string evaluation during template execution.”
“IsTrue reports whether the value is 'true', in the sense of not the zero of its type, and whether the value has a meaningful truth value. This is the definition of truth used by if and other such actions.”
The template client provides data that map to template actions (https://github.com/golang/go/blob/release-branch.go1.9/src/text/template/parse/node.go).
“Register publishes in the server the set of methods of the receiver value that satisfy the following conditions:
A net/rpc.ClientCodec is responsible for converting the rpc interface{} argument set into a representation that is understandable by the server being called.
Deep Equality is defined for arrays, structs, func, interface, map, pointer, slice, numbers, bools, strings, and channels.
“Swapper returns a function that swaps the elements in the provided slice. Swapper panics if the provided interface is not a slice.”
“TypeOf returns the reflection Type that represents the dynamic type of i. If i is a nil interface value, TypeOf returns nil.”
“ValueOf returns a new Value initialized to the concrete value stored in the interface i. ValueOf(nil) returns the zero Value.”
“Interface returns v's current value as an interface{}. It is equivalent to: var i interface{} = (v's underlying value). It panics if the Value was obtained by accessing unexported struct fields.”
“KeepAlive marks its argument as currently reachable. This ensures that the object is not freed, and its finalizer is not run, before the point in the program where KeepAlive is called.”
“The argument obj must be a pointer to an object allocated by calling new, by taking the address of a composite literal, or by taking the address of a local variable. The argument finalizer must be a function that takes a single argument to which obj's type can be assigned, and can have arbitrary ignored return values. If either of these is not true, SetFinalizer may abort the program.”
“Add adds the current execution stack to the profile, associated with value. Add stores value in an internal map, so value must be suitable for use as a map key and will not be garbage collected until the corresponding call to Remove. Add panics if the profile already contains a stack for value.”
“Slice sorts the provided slice given the provided less function…The function panics if the provided interface is not a slice.”
“Check looks for an input to f, any function that returns bool, such that f returns false. It calls f repeatedly, with arbitrary values for each argument. If f returns false on a given input, Check returns that input as a *CheckError.”
“Parse returns a map from template name to parse.Tree, created by parsing the templates described in the argument string. The top-level template will be given the specified name. If an error is encountered, parsing stops and an empty map is returned with the error.” |
This is interesting to look at. I took a shot at classifying these: Functions that accept ~anything.Prototypical example: Functions that store ~anything, and return it later.Prototypical example: Functions operating on a broad spectrum of types.Prototypical example: Containers.Prototypical example: Algorithms.Prototypical example: Functions operating on a specific set of types.Prototypical example: |
In every case interface{} is used to describe a type that cannot be fully described programmatically with Go. I propose the solution to put each interface{} behind a type ( |
What are the constraints that would be documented? |
The constraint would be that it can be formatted for %v. The type documentation would be a place to describe fmt verbs which are the constraint for the Printf functions. Printf documentation would hold more value than the Print's "anything works". Ideally the compiler would verify each input type with the corresponding format string verb, but since this is not possible it would be described as part of the input type documentation. For fmt and probably most cases this would just be moving documentation around, but the added value is the ability to guess right away when looking at a function/method signature what the type constraints are beyond "you can try anything". |
@neild |
@kardianos Apologies, I meant to write |
@neild Same thing still applies, but the docs are outdated now. Shoot. Gotta send a CL. |
Change https://golang.org/cl/84636 mentions this issue: |
The driver.Value type may be more then the documented 6 types if the database driver supports it. Document that fact. Updates #23077 Change-Id: If7e2112fa61a8cc4e155bb31e94e89b20c607242 Reviewed-on: https://go-review.googlesource.com/84636 Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
In addition to @neild's classification above, I would like to share my learnings about the empty interface in the Usage:
Purpose
Moreover, the calls to these functions NOTE: This explanation is to the best of my understanding and there might be deeper implications which I might be overlooking at the moment because of my limited experience in writing Go code. |
Thanks for all the investigation. This is going to be entirely dependent on generics. And, of course, there are cases where do need to keep the empty interface, as in |
This initial proposal is to remove all uses of the empty interface (interface{}) from the standard library for Go 2.
@chowey listed these in the generics proposal discussion (#15292):
The type interface{} represents an item that has no required ability. The above cases use it to store items of any type in a container, but an item with no required ability may not be storable. encoding/json uses it with the assumption that the item has a specific kind of structure which in my opinion breaks the annotation of “no required ability”.
Compile-time type safety is a key feature of Go and the standard library should have checks in every case. Using reflect (run-time type safety) or wrappers (compile-time type safety) incur performance or readability costs. In both cases a type assertion is required. Code generation is probably excessively complex for standard use and Go already explicitly does not have regular C-like preprocessor directives.
This is the key reason to implement generics, or there may be alternative Go 1 implementations that improve over the use of interface{}, or these necessary types could be added to the built-in type list at considerable expense.
The text was updated successfully, but these errors were encountered: