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