go by example part 3
This commit is contained in:
parent
c1b7c64da2
commit
c5983176c5
48
5-go-by-example/21-embedding.go
Normal file
48
5-go-by-example/21-embedding.go
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
// embedding of structs and interfaces to express a more seamless composition of types
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
type base struct {
|
||||||
|
num int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (b base) describe() string {
|
||||||
|
return fmt.Sprintf("base with num=%v", b.num)
|
||||||
|
}
|
||||||
|
|
||||||
|
// container embeds a base
|
||||||
|
// embedding looks like a field without a name
|
||||||
|
type container struct {
|
||||||
|
base
|
||||||
|
str string
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// when creating structs with literals, initialize the embedding explicitly
|
||||||
|
co := container{
|
||||||
|
base: base{
|
||||||
|
num: 1,
|
||||||
|
},
|
||||||
|
str: "some name",
|
||||||
|
}
|
||||||
|
|
||||||
|
// access the base’s fields directly
|
||||||
|
fmt.Printf("co={num: %v, str: %v}\n", co.num, co.str)
|
||||||
|
|
||||||
|
// full path using the embedded type name
|
||||||
|
fmt.Println("also num:", co.base.num)
|
||||||
|
|
||||||
|
// container embeds base
|
||||||
|
// methods of base also become methods of a container
|
||||||
|
fmt.Println("describe:", co.describe())
|
||||||
|
|
||||||
|
type describer interface {
|
||||||
|
describe() string
|
||||||
|
}
|
||||||
|
|
||||||
|
// container implements describer interface because it embeds base
|
||||||
|
var d describer = co
|
||||||
|
fmt.Println("describer:", d.describe())
|
||||||
|
}
|
59
5-go-by-example/22-errors.go
Normal file
59
5-go-by-example/22-errors.go
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
)
|
||||||
|
|
||||||
|
// by convention, errors are the last return value and have type error, a built-in interface
|
||||||
|
func f1(arg int) (int, error) {
|
||||||
|
if arg == 42 {
|
||||||
|
// errors.New constructs a basic error value with the given error message
|
||||||
|
return -1, errors.New("can't work with 42")
|
||||||
|
}
|
||||||
|
// a nil value in the error position indicates that there was no error
|
||||||
|
return arg + 3, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// it’s possible to use custom types as errors by implementing the Error() method on them
|
||||||
|
type argError struct {
|
||||||
|
arg int
|
||||||
|
prob string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *argError) Error() string {
|
||||||
|
return fmt.Sprintf("%d - %s", e.arg, e.prob)
|
||||||
|
}
|
||||||
|
|
||||||
|
func f2(arg int) (int, error) {
|
||||||
|
if arg == 42 {
|
||||||
|
// use &argError syntax to build a new struct
|
||||||
|
return -1, &argError{arg, "can't work with it"}
|
||||||
|
}
|
||||||
|
return arg + 3, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// loops below test out each of our error-returning functions
|
||||||
|
for _, i := range []int{7, 42} {
|
||||||
|
if r, e := f1(i); e != nil {
|
||||||
|
fmt.Println("f1 failed:", e)
|
||||||
|
} else {
|
||||||
|
fmt.Println("f1 worked:", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, i := range []int{7, 42} {
|
||||||
|
if r, e := f2(i); e != nil {
|
||||||
|
fmt.Println("f2 failed:", e)
|
||||||
|
} else {
|
||||||
|
fmt.Println("f2 worked:", r)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// programmatically use the data in a custom error
|
||||||
|
_, e := f2(42)
|
||||||
|
if ae, ok := e.(*argError); ok {
|
||||||
|
fmt.Println(ae.arg)
|
||||||
|
fmt.Println(ae.prob)
|
||||||
|
}
|
||||||
|
}
|
32
5-go-by-example/23-goroutines.go
Normal file
32
5-go-by-example/23-goroutines.go
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
// A goroutine is a lightweight thread of execution
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func f(from string) {
|
||||||
|
for i := 0; i < 3; i++ {
|
||||||
|
fmt.Println(from, ":", i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// normal function call f(s), running synchronously
|
||||||
|
f("direct")
|
||||||
|
|
||||||
|
// invoke functions in a goroutine, use go f(s)
|
||||||
|
// this new goroutine will execute concurrently with the calling one
|
||||||
|
go f("goroutine")
|
||||||
|
|
||||||
|
// start a goroutine for an anonymous function call
|
||||||
|
go func(msg string) {
|
||||||
|
fmt.Println(msg)
|
||||||
|
}("going")
|
||||||
|
|
||||||
|
// the two function calls are running asynchronously in separate goroutines now
|
||||||
|
// Wait for them to finish (alternate use WaitGroup)
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
fmt.Println("done")
|
||||||
|
}
|
16
5-go-by-example/24-channels.go
Normal file
16
5-go-by-example/24-channels.go
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
// Channels are the pipes that connect concurrent goroutines
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// create new channel with make(chan val-type)
|
||||||
|
messages := make(chan string)
|
||||||
|
|
||||||
|
// send a value into a channel, here "ping"
|
||||||
|
go func() { messages <- "ping" }()
|
||||||
|
|
||||||
|
// receives a value from the channel
|
||||||
|
msg := <-messages
|
||||||
|
fmt.Println(msg)
|
||||||
|
}
|
18
5-go-by-example/25-channel-buffering.go
Normal file
18
5-go-by-example/25-channel-buffering.go
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
// By default channels are unbuffered, meaning that they will only accept sends (chan <-) if there is a corresponding receive (<- chan) ready to receive the sent value
|
||||||
|
// Buffered channels accept a limited number of values without a corresponding receiver for those values
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// create channel of strings buffering up to 2 values
|
||||||
|
messages := make(chan string, 2)
|
||||||
|
|
||||||
|
// send values into the channel without corresponding concurrent receive
|
||||||
|
messages <- "buffered"
|
||||||
|
messages <- "channel"
|
||||||
|
|
||||||
|
// receive values as usual
|
||||||
|
fmt.Println(<-messages)
|
||||||
|
fmt.Println(<-messages)
|
||||||
|
}
|
24
5-go-by-example/26-channel-synchronization.go
Normal file
24
5-go-by-example/26-channel-synchronization.go
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
// synchronize execution across goroutines
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// the done channel will be used to notify another goroutine that this function’s work is done
|
||||||
|
func worker(done chan bool) {
|
||||||
|
fmt.Print("working...")
|
||||||
|
time.Sleep(time.Second)
|
||||||
|
fmt.Println("done")
|
||||||
|
// Send a value to notify "done"
|
||||||
|
done <- true
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// start a worker goroutine, giving it the channel to notify on
|
||||||
|
done := make(chan bool, 1)
|
||||||
|
go worker(done)
|
||||||
|
// block until receive notification from the worker on the channel
|
||||||
|
<-done
|
||||||
|
}
|
23
5-go-by-example/27-channel-directions.go
Normal file
23
5-go-by-example/27-channel-directions.go
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
// specify if a channel is meant to only send or receive values
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
// ping function only accepts a channel for sending values
|
||||||
|
func ping(pings chan<- string, msg string) {
|
||||||
|
pings <- msg
|
||||||
|
}
|
||||||
|
|
||||||
|
// pong function accepts one channel for receives (pings) and a second for sends (pongs)
|
||||||
|
func pong(pings <-chan string, pongs chan<- string) {
|
||||||
|
msg := <-pings
|
||||||
|
pongs <- msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
pings := make(chan string, 1)
|
||||||
|
pongs := make(chan string, 1)
|
||||||
|
ping(pings, "passed message")
|
||||||
|
pong(pings, pongs)
|
||||||
|
fmt.Println(<-pongs)
|
||||||
|
}
|
34
5-go-by-example/28-select.go
Normal file
34
5-go-by-example/28-select.go
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// select lets you wait on multiple channel operations
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// select across two channels
|
||||||
|
|
||||||
|
c1 := make(chan string)
|
||||||
|
c2 := make(chan string)
|
||||||
|
|
||||||
|
// each channel will receive a value after some amount of time, to simulate
|
||||||
|
go func() {
|
||||||
|
time.Sleep(1 * time.Second)
|
||||||
|
c1 <- "one"
|
||||||
|
}()
|
||||||
|
go func() {
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
c2 <- "two"
|
||||||
|
}()
|
||||||
|
|
||||||
|
// use select to await both of these values simultaneously
|
||||||
|
for i := 0; i < 2; i++ {
|
||||||
|
select {
|
||||||
|
case msg1 := <-c1:
|
||||||
|
fmt.Println("received", msg1)
|
||||||
|
case msg2 := <-c2:
|
||||||
|
fmt.Println("received", msg2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
41
5-go-by-example/29-timeouts.go
Normal file
41
5-go-by-example/29-timeouts.go
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
// bound execution time
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// returns its result on a channel c1 after 2s
|
||||||
|
// channel is buffered, so the send in the goroutine is nonblocking
|
||||||
|
c1 := make(chan string, 1)
|
||||||
|
go func() {
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
c1 <- "result 1"
|
||||||
|
}()
|
||||||
|
|
||||||
|
// select implementing a timeout
|
||||||
|
// res awaits the result and <-time
|
||||||
|
// After awaits a value to be sent after the timeout
|
||||||
|
// select proceeds with the first receive that’s ready
|
||||||
|
select {
|
||||||
|
case res := <-c1:
|
||||||
|
fmt.Println(res)
|
||||||
|
case <-time.After(1 * time.Second):
|
||||||
|
fmt.Println("timeout 1")
|
||||||
|
}
|
||||||
|
|
||||||
|
// allow a longer timeout of 3s, then the receive from c2 will succeed and print the result
|
||||||
|
c2 := make(chan string, 1)
|
||||||
|
go func() {
|
||||||
|
time.Sleep(2 * time.Second)
|
||||||
|
c2 <- "result 2"
|
||||||
|
}()
|
||||||
|
select {
|
||||||
|
case res := <-c2:
|
||||||
|
fmt.Println(res)
|
||||||
|
case <-time.After(3 * time.Second):
|
||||||
|
fmt.Println("timeout 2")
|
||||||
|
}
|
||||||
|
}
|
42
5-go-by-example/30-non-blocking-channel-.go
Normal file
42
5-go-by-example/30-non-blocking-channel-.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// Basic sends and receives on channels are blocking
|
||||||
|
// use select with a default clause to implement non-blocking
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
messages := make(chan string)
|
||||||
|
signals := make(chan bool)
|
||||||
|
|
||||||
|
// non-blocking receive
|
||||||
|
// if a value is available on messages then select will take the <-messages case with that value
|
||||||
|
// if not it will immediately take the default case
|
||||||
|
select {
|
||||||
|
case msg := <-messages:
|
||||||
|
fmt.Println("received message", msg)
|
||||||
|
default:
|
||||||
|
fmt.Println("no message received")
|
||||||
|
}
|
||||||
|
|
||||||
|
// non-blocking send
|
||||||
|
// msg cannot be sent to the messages channel, because the channel has no buffer and there is no receiver
|
||||||
|
// Therefore the default case is selected.
|
||||||
|
msg := "hi"
|
||||||
|
select {
|
||||||
|
case messages <- msg:
|
||||||
|
fmt.Println("sent message", msg)
|
||||||
|
default:
|
||||||
|
fmt.Println("no message sent")
|
||||||
|
}
|
||||||
|
|
||||||
|
// use multiple cases above the default clause to implement a multi-way non-blocking select
|
||||||
|
// here non-blocking receives on both messages and signals
|
||||||
|
select {
|
||||||
|
case msg := <-messages:
|
||||||
|
fmt.Println("received message", msg)
|
||||||
|
case sig := <-signals:
|
||||||
|
fmt.Println("received signal", sig)
|
||||||
|
default:
|
||||||
|
fmt.Println("no activity")
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user