This is a creation in Article, where the information may have evolved or changed.
Methods
Pointers vs. Values
Methods can defined for any named type, is not a pointer or an interface; The receiver does not has the to is a struct.
In addition to pointers and interfaces (interface) You can define methods for any custom type and this custom type may not be a struct struct
In the discussion of slices above, we wrote an Append function. We can define it as a method on slices instead. To does this, we first declare a named type to which we can bind the method, and then make the receiver for the method a Val UE of that type.
When we were talking about slice, we wrote a append function, and we passed the slice that we needed to modify as arguments to the function append we can also define the method for slice but we need to define the alias for slice and define the object of the method as the type This can be done directly through the slice. Append to add elements to the slice:
Type Byteslice []byte//alias func (Slice byteslice) Append (data []byte) []byte { //Body exactly the same as above}
This still requires the method to return the updated slice. We can eliminate that clumsiness by redefining the method to take a pointer to a ByteS Lice as its receiver, so the method can overwrite the caller ' s slice.
The above code defines the method for Byteslice but still needs to return the modified slice in order to avoid passing the slice back and forth, we can define the method for the Byteslice pointer type:
Func (P *byteslice) Append (data []byte) { slice: = *p //Body as above, without the return. *p = slice}
In fact, we can do even better. If we modify our function so it looks like a standard Write method that like this,
Of course, there's still room for improvement. We can implement IO. Writer interface:
Func (P *byteslice) Write (data []byte) (n int, err error) { slice: = *p //Again as above. *p = Slice return len (data), nil}
Then the type *byteslice satisfies the standard interface io. Writer, which is handy. For instance, we can print into one.
Implementation of IO. After the writer interface we can use it this way:
var b byteslice Fmt. fprintf (&b, "This Hour has%d days\n", 7)
We Pass the address of a byteslice because only *byteslice satisfies io. Writer. The rule about pointers vs. values for receivers are that value methods can being invoked on pointers and values, but pointer Methods can only is invoked on pointers. This is because pointer methods can modify the receiver; Invoking them on a copy of the value would cause those modifications to be discarded.
Pointers must be passed so that IO can be satisfied. Writer interface when defining a method, if the method object is a value of a type, it can be called by the type value or by the type pointer, but when the method is defined, the object is a pointer of a type, and only the pointer of that type can trigger the method call.
By the-the-the-slice of bytes is implemented by bytes. Buffer.
Bytes Slice has implemented the writer interface See bytes. Buffer
Interfaces and other types
Interfaces
Interfaces in Go provide a-specify the behavior of an object:if something can do this and then it C An is used here. We ' ve seen a couple of simple examples already; Custom printers can implemented by a String method while fprintf can generate OUTPU T to anything with a Write method. Interfaces with only one or both methods is common in Go code, and is usually given a name derived from the method, such As io. Writer for something that implements Write.
Go can use interfaces to gain object-oriented capabilities such as the ability to customize its output by defining a string method for a type go often sees interfaces with only one or two methods defined. The names of these interfaces are related to the method that it defines, such as IO. The writer interface implements the Write method and the interface is called IO. Writer
A type can implement multiple interfaces. For instance, a collection can is sorted by the routines of the package Sort if it implements sort. Interface, which contains Len (), Less (i, J int) bool, and Swap (i, J int), and it could also has a custom Formatter. In this contrived example Sequence satisfies both.
A type can implement multiple interfaces at the same time, such as sorting an array if the sort in the sort package is implemented. Interface interface It can directly use the sort package's correlation function and can also implement the format of the string function custom output
//Methods required by sort. Interface.
Func (S Sequence) len () int { return Len (s)}func (s Sequence) Less (i, J int) bool { return s[i] < S[j]}func (s Sequence) Swap (i, J int) { //Method for printing-sorts the elements before printing.
Func (S Sequence) string () string { sort. Sort (s) str: = "[" for I, Elem: = range S { If i > 0 { str + = " } str + = FMT. Sprint (elem) } return str + "]"}
Conversions
The String method of Sequence is recreating the work that sprint< c14> already does for slices. We can share the effort if we convert the Sequence to a plain []int before Cal Lingsprint.
In the example above, the string method repeats the Sprint's work sprint has defined the output method for slice we can convert the sequence type to []int and then use the sprint directly to output
Func (S Sequence) string () string { sort. Sort (s) return to FMT. Sprint ([]int (s))}
The conversion causes s To being treated as an ordinary slice and therefore receive the default formatting. Without the conversion, Sprint would find the String method of Sequence and recur indefinitely. Because the types (Sequence and []int] is the same if we ignore the type name, it's legal to convert between them. The conversion doesn ' t create a new value, it just temporarily acts as though the existing value has a new type. (There is other legal conversions, such as from an integer to floating point, that is do create a new value.)
The type conversion in the above code causes S to be treated as a normal slice to handle the output with the default format if the sprint does not convert the sequence string method and then goes into the dead loop sequence and []int except the name is different from the others. So it's possible to convert between them, and it's safe. The conversion operation does not create a new value it only temporarily treats the current type as a different type.
It's an idiom on Go programs to convert the type of an expression to access a different set of methods. As an example, we could use the existing type sort. Intslice To reduce the entire example to this:
The methods and types in go are bound together so that other types of methods can be used through type conversion see the following example we can use sort. The Intslice method streamlines the above ordering and outputting code to:
Type Sequence []int//Method for Printing-sorts the elements before Printingfunc (s Sequence) string () string { sort . Intslice (s). Sort () return to FMT. Sprint ([]int (s))}
Now, instead of have Sequence Implement multiple interfaces (sorting and printing), we ' re using the AB Ility of a data item to being converted to multiple types (sequence,sort. Intslice and []int), each of which does some part of the job. That's more unusual in practice but can is effective.
In this example, we do not need to implement multiple interfaces, such as sorting and output, we can use type conversions to work with other types of methods
Generality
If a type exists only to implement an interface and have no exported methods beyond that interface, there are no need to exp ORT the type itself. Exporting just the interface makes it clear that it's the behavior that matters, not the implementation, and so other IM Plementations with different properties can mirror the behavior of the original type. It also avoids the need to repeat the documentation on every instance of a common method.
If you define a type just to implement an interface and you do not need to export other methods except for the methods defined in this interface, you can export only the interface. The additional benefit of doing this is to emphasize the function behavior defined in the interface rather than the specific implementation and the extra advantage of doing so at the same time is that you do not need to write the document repeatedly
In such cases, the constructor should return an interface value rather than the implementing type. As an example, in the hash libraries both CRC32. Newieee and Adler32. New Return the interface type hash. Hash32. Substituting the CRC-32 algorithm for Adler-32 in a Go program requires only changing the constructor call; The rest of the code is unaffected by the change of algorithm.
In this case, the constructor needs to return an interface instead of implementing the type of the interface for example Hachiku CRC32. Both NEWIEEE and Adler32.new return interface types Hash.hash32 If you want to replace the CRC32 algorithm with Adler32, you only need to call another constructor, and no other code needs to be changed.
A Similar approach allows the streaming cipher algorithms in the various crypto packages to be separated From the block ciphers they chain together. The block interface in the crypto/cipher Package Specifies the behavior of a Block CIP Her, which provides encryption of a single block of data. Then, by analogy with the bufio package, cipher packages that implement this interface can is used to co NStruct streaming ciphers, represented by the Stream interface, without knowing the details of the block Encryption.
A similar method can be used to separate the stream encryption algorithm from the block encryption algorithm of various cryptographic packets. The block interface in the Crypto/cipher package defines the behavior of the Block encryption algorithm (the method of encrypting a single block of data) and the Bufio package. The cipher package can implement this interface to guide the stream encryption algorithm (stream interface) Note: Here you need to look at the encryption algorithm most of the forgotten
The crypto/cipher interfaces look like this:
The Crypto/cipher interface looks like this.
Type Block Interface { BlockSize () int Encrypt (src, DST []byte) Decrypt (src, DST []byte)}type Stream interface { Xorkeystream (DST, src []byte)}
Here's the definition of the counter mode (CTR) stream, which turns a block cipher into a streaming cipher; Notice that the block cipher's details are abstracted away:
The following code is the definition of the count-mode stream, which converts the block cipher to stream cipher
NEWCTR returns a Stream that encrypts/decrypts using the given Block in//counter mode. The length of IV must be the same as the block ' s Block Size.func newctr (block block, iv []byte) Stream
Newctr applies not just to one specific encryption algorithm and data source if any implementation of the Block interface and any Stream. Because They return interface values, replacing CTR encryption with other encryption modes are a localized change. The constructor calls must is edited, but because the surrounding code must treat the result is only as a Stream, it Won ' t notice the difference.
NEWCTR can be used on any type that implements the block or stream interface because the interface value is returned to replace the CRT encryption algorithm only needs to be changed in one place.
Interfaces and methods
Since almost anything can has methods attached, almost anything can satisfy an interface. One illustrative example is in the http package, which defines the Handler interface. Any object that implements Handler can serve HTTP requests.
Any type can define a method HTTP package is a good example it defines the handler interface any object that implements the handler interface can handle HTTP requests
Type Handler Interface { servehttp (Responsewriter, *request)}
Responsewriter is itself a interface that provides access to the methods needed to return the response to the CL Ient. Those methods include the standard Write method, so an http. Responsewriter can used wherever an io. Writer can be used. Request is a struct containing a parsed representation of the request from the client.
Responsewriter itself is an interface it provides methods to respond to customer requests These methods include the standard writer method so any IO can be used. You can use HTTP in the writer's Place . Responsewriter request is a struct that contains an already resolved HTTP request
For brevity, let's ignore POSTs and assume HTTP requests is always GETs; That's simplification does not affect the handlers is set up. Here's a trivial but complete implementation of a handler to count the number of times the page is visited.
Assuming that the HTTP request to be processed is only a GET method, this assumption does not affect the implementation of the HTTP request handler function The following code is simple but it can handle the number of times the HTTP request Record page is accessed
Simple counter Server.type counter struct { n int}func (Ctr *counter) servehttp (w http. Responsewriter, req *http. Request) { ctr.n++ fmt. fprintf (W, "counter =%d\n", CTR.N)}
(Keeping with our theme, note how fprintf can print to an http. Responsewriter.) For reference, here's how to attach such a server to a node on the URL tree.
The following code maps the/counter to a specific handler we just defined:
Import "Net/http" ... ctr: = new (Counter) HTTP. Handle ("/counter", CTR)
But why make Counter a struct? An integer was all that ' s needed. (The receiver needs to being a pointer so the increment are visible to the caller.)
Why do we need to define counter as a struct? We just need an integer.
Simpler Counter Server.type counter IntFunc (Ctr *counter) servehttp (w http. Responsewriter, req *http. Request) { *ctr++ fmt. fprintf (W, "counter =%d\n", *ctr)}
What is the If your program have some internal state, that needs to being notified that a page has been visited? Tie a channel to the Web page.
A page is accessed and your program has some internal state that you need to know about the action you can do with the channel:
A channel that sends a notification in each visit.//(Probably want the channel to be buffered.) Type Chan Chan *http. Requestfunc (ch Chan) servehttp (w http. Responsewriter, req *http. Request) { ch <-req fmt. Fprint (W, "Notification Sent")}
Finally, let's say we wanted to present on /args The arguments used when invoking the server binary. It's easy-to-write a function to print the arguments.
If we want to give the parameters of the startup Server a function prototype for the command arguments when accessing Http://host/args:
Func argserver () { for _, S: = Range OS. Args { FMT. Println (s) }}
How does we turn that to an HTTP server? We could make argserver A method of the some type whose value we ignore, but there ' a cleaner the. Since We can define a method for any type except pointers and interfaces, we can write a method for a function. The http package contains this code:
But how do you get it to handle HTTP requests? There is a way to define a type, such as an empty struct, and implement the handler interface like the code above, but we already know that you can define method functions for any type except pointers and interfaces. We can define methods directly to the function The following code can be found in the HTTP package:
The Handlerfunc type is a adapter to allow the use of//ordinary functions as HTTP handlers. If F is a function//with the appropriate signature, Handlerfunc (f) are a//Handler object that calls F.type Handlerfunc Fu NC (Responsewriter, *request)//Servehttp calls F (c, req). Func (f Handlerfunc) servehttp (w responsewriter, req *request) { C1/>f (W, req)}
Handlerfunc is an adapter that allows normal functions to handle HTTP requests if f is a function then Handlerfunc (f) is handler object This object calls F
Handlerfunc is a type with a method, servehttp, so values of this type can serve HTTP requests. Look at the implementation of the Method:the receiver is a function, F, and the method calls F. That could seem odd but it's not this different from, say, the receiver being a channel and the method sending on the Channe L.
Handlerfunc is a function type the value of this type can handle the HTTP request look at its implementation it acts on the function f and then calls the F function in the method it's a little strange, but essentially the same as the other type methods.
To make argserver into a HTTP server, we first modify it to has the right signature.
Rewrite the argserver as an HTTP server first we have to change the signature of the function, or the handlerfunc won't fit.
Argument server.func Argserver (w http. Responsewriter, req *http. Request) { for _, S: = Range OS. Args { FMT. Fprintln (W, s) }}
Argserver now have same signature as Handlerfunc, so it can is converted to the type to access its Metho DS, just as we converted Sequence to intslice to Accessintslice.sort. The code to set it concise:
after rewriting Argserver 's signature is the same as Handlerfunc, you can convert it to a Handlerfunc type to use the Handlerfunc method:
http. Handle ("/args", http. Handlerfunc (Argserver))
When someone visits the page /args, the handler installed at that page had value argserver and Type handlerfunc. The HTTP server would invoke the method servehttp of the type, with argserver as the R Eceiver, which'll in turn call argserver (via the invocation F (c, req) inside< c13> handlerfunc.servehttp). The arguments would then be displayed.
The value of handler that handles this URL when accessing Http://host/args is the Handlerfunc method where the Argserver type is handlerfunc the HTTP server calls the Serverhttp type
In this section we had made an HTTP server from a struct, an integer, a channel, and a function, all because interfaces a Re just sets of methods, which can be defined for (almost) any type.
In this section, we write an HTTP server with the structure integer channel and function to integrate these reasons is that the interface is a set of methods can almost give any type definition method