initial commit
This commit is contained in:
commit
493ce92e02
62 changed files with 1213 additions and 0 deletions
BIN
arrays/arrays
Executable file
BIN
arrays/arrays
Executable file
Binary file not shown.
32
arrays/arrays.go
Normal file
32
arrays/arrays.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main(){
|
||||
//here we create an array a that will hold exactly 5 ints.
|
||||
// arrays are typed and statically lengthed
|
||||
// by default an array is zero-valued which for ints is just 0 in all positions
|
||||
var a [5]int
|
||||
fmt.Println("emp:",a)
|
||||
|
||||
//we can set a value at an index using the arrray[index] = value syntax
|
||||
//and get the value @ an index with the array[index] syntax
|
||||
//the builtin len returns the length of an array.
|
||||
a[4] = 100
|
||||
fmt.Println("set:", a)
|
||||
fmt.Println("get:", a[4])
|
||||
fmt.Println("len:", len(a))
|
||||
|
||||
//the following can be used to declare and initalize the valaues
|
||||
// of an array on a single line
|
||||
b := [5]int{1,2,3,4,5}
|
||||
fmt.Println("dcl:", b)
|
||||
|
||||
var twoD [2][3]int
|
||||
for i := 0; i < 2; i++ {
|
||||
for j := 0; j < 3; j++ {
|
||||
twoD[i][j] = i+j
|
||||
}
|
||||
}
|
||||
fmt.Println("2d: ", twoD)
|
||||
}
|
||||
BIN
channel-buffering/chanbuffer
Executable file
BIN
channel-buffering/chanbuffer
Executable file
Binary file not shown.
27
channel-buffering/chanbuffer.go
Normal file
27
channel-buffering/chanbuffer.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main(){
|
||||
|
||||
//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
|
||||
|
||||
|
||||
//here we make a channel of strings buffering up to 2 values
|
||||
messages := make(chan string, 2)
|
||||
|
||||
//because this channel is buffered,
|
||||
//we can send these values into the channel without a
|
||||
//corresponding concurrent receive
|
||||
messages <- "buffered"
|
||||
messages <- "channel"
|
||||
|
||||
|
||||
//later we can receive these two values as usual
|
||||
fmt.Println(<-messages)
|
||||
fmt.Println(<-messages)
|
||||
}
|
||||
BIN
channel-directions/channeldirections
Executable file
BIN
channel-directions/channeldirections
Executable file
Binary file not shown.
25
channel-directions/channeldirections.go
Normal file
25
channel-directions/channeldirections.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
//this func only accepts a channel for sending values.
|
||||
//it would be a compile-time error
|
||||
//to try to receive on this channel.
|
||||
func ping(pings chan<- string,msg string){
|
||||
pings <- msg
|
||||
}
|
||||
|
||||
//this 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)
|
||||
}
|
||||
BIN
channel-sync/channelsync
Executable file
BIN
channel-sync/channelsync
Executable file
Binary file not shown.
30
channel-sync/channelsync.go
Normal file
30
channel-sync/channelsync.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
//we can use channels to synchronize execution across goroutines.
|
||||
//here is an example of using a blocking receive to wait for a goroutine
|
||||
//to finish whenwaiting for multiple goroutines to finish you may prefer to use a WaitGroup
|
||||
|
||||
|
||||
//This is the function we'll run in a goroutine.
|
||||
//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")
|
||||
|
||||
//start a worker goroutine, giving it the channel to notify on.
|
||||
done <- true
|
||||
}
|
||||
|
||||
func main(){
|
||||
done := make(chan bool,1)
|
||||
go worker(done)
|
||||
|
||||
//block until we receive a notification from the worker on the channel
|
||||
<-done
|
||||
}
|
||||
BIN
channels/channels
Executable file
BIN
channels/channels
Executable file
Binary file not shown.
22
channels/channels.go
Normal file
22
channels/channels.go
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
|
||||
//Channels are the pipes that connect concurrent goroutines.
|
||||
//you can send values into channels
|
||||
//form one goroutine and receive those values into another goroutine
|
||||
func main(){
|
||||
//create a new channel with make(chan val-type)
|
||||
//Channels are typed bt the values they convey
|
||||
messages := make(chan string)
|
||||
|
||||
//send a value into a channel using the channel <- syntax.
|
||||
//here we send "ping" to the messages channel we made above, from a new goroutine
|
||||
go func() { messages <- "ping"}()
|
||||
|
||||
//the <- channel syntax receives a value from the channel.
|
||||
// here we'll receive the "ping" message we sent above and print it out
|
||||
msg := <- messages
|
||||
fmt.Println(msg)
|
||||
}
|
||||
BIN
closures/closures
Executable file
BIN
closures/closures
Executable file
Binary file not shown.
30
closures/closures.go
Normal file
30
closures/closures.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// This function returns another function,
|
||||
// which we define anonymously in the body of the intSeq.
|
||||
// The returned function closes over the variable i to form a closure
|
||||
func intSeq() func() int {
|
||||
i := 0
|
||||
return func() int {
|
||||
i++
|
||||
return i
|
||||
}
|
||||
}
|
||||
|
||||
func main(){
|
||||
//we call intSeq assigning the result(a function) to nextInt.
|
||||
// This function value captures its own i value, which will be updated
|
||||
// each time we call nextInt
|
||||
nextInt := intSeq()
|
||||
|
||||
// see the effect of the closure by calling nextInt a few times.
|
||||
fmt.Println(nextInt())
|
||||
fmt.Println(nextInt())
|
||||
fmt.Println(nextInt())
|
||||
|
||||
// to confirm that the state is unique to the instance create a new instance
|
||||
newInts := intSeq()
|
||||
fmt.Println(newInts())
|
||||
}
|
||||
BIN
constants/constants
Executable file
BIN
constants/constants
Executable file
Binary file not shown.
24
constants/constants.go
Normal file
24
constants/constants.go
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
|
||||
//const declares a constant value.
|
||||
const s string = "constant"
|
||||
|
||||
func main(){
|
||||
fmt.Println(s)
|
||||
// A const statement can appear anywhere a var statement can.
|
||||
const n = 500000000
|
||||
// Constant expressions perform arithmetic with arbitrary precision.
|
||||
const d = 3e20 / n
|
||||
// A numeric constant has no type until it’s given one, such as by an explicit conversion.
|
||||
fmt.Println(int64(d))
|
||||
// A number can be given a type by using it
|
||||
// in a context that requires one, such
|
||||
// as a variable assignment or function call.
|
||||
// For example, here math.Sin expects a float64.
|
||||
fmt.Println(math.Sin(n))
|
||||
}
|
||||
BIN
errors/errors
Executable file
BIN
errors/errors
Executable file
Binary file not shown.
68
errors/errors.go
Normal file
68
errors/errors.go
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
//In Go its idiomatic to communicate errors via an explicit, seperate return value.
|
||||
//this contrasts with the exceptions used in languages like java and Ruby
|
||||
//and the verloaded single result/error value sometimes used in C.
|
||||
//Go's approach makes it easy to see which functions return errors and to
|
||||
//handle them using the same language constructs employed for any other non-error tasks
|
||||
|
||||
|
||||
// by convention, errors are the last return value and have type error, a builtin 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("cant work with 42")
|
||||
}
|
||||
//a nil value in the error position indicates that there was no error
|
||||
return arg + 3, nil
|
||||
}
|
||||
|
||||
// its possible to use custom types as errors by implementing Error() method on them.
|
||||
// Heres a variant on the example above that uses a custom error type
|
||||
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 {
|
||||
return -1, &argError{arg, "can't work with it"}
|
||||
}
|
||||
return arg+3,nil
|
||||
}
|
||||
|
||||
func main(){
|
||||
//The two loops below test out each of our error-returning
|
||||
//functions. Note that the use of an inline error check on the if line is a common idiom in Go code.
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
//if you want to programmatically use the data in a custom error, you'll need to get the error
|
||||
// as an instance of the custom error type via type assertion
|
||||
_, e := f2(42)
|
||||
if ae, ok:= e.(*argError); ok {
|
||||
fmt.Println(ae.arg)
|
||||
fmt.Println(ae.prob)
|
||||
}
|
||||
}
|
||||
61
flake.lock
generated
Normal file
61
flake.lock
generated
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1701680307,
|
||||
"narHash": "sha256-kAuep2h5ajznlPMD9rnQyffWG8EM/C73lejGofXvdM8=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "4022d587cbbfd70fe950c1e2083a02621806a725",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1702312524,
|
||||
"narHash": "sha256-gkZJRDBUCpTPBvQk25G0B7vfbpEYM5s5OZqghkjZsnE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a9bf124c46ef298113270b1f84a164865987a91c",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
18
flake.nix
Normal file
18
flake.nix
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
description = "Go development environment";
|
||||
inputs.flake-utils.url = "github:numtide/flake-utils";
|
||||
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||
outputs = { self, flake-utils, nixpkgs }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let pkgs = nixpkgs.legacyPackages.${system}; in
|
||||
{
|
||||
devShells.default = with pkgs; mkShell {
|
||||
packages = [
|
||||
go
|
||||
];
|
||||
shellHook = ''
|
||||
'';
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
32
for/for.go
Normal file
32
for/for.go
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main(){
|
||||
i := 1
|
||||
|
||||
// for with a single condition
|
||||
for i <= 3 {
|
||||
fmt.Println(i)
|
||||
i = i+1
|
||||
}
|
||||
|
||||
// Initial/Condition/After classic style loop
|
||||
for j := 7; j <= 9; j++ {
|
||||
fmt.Println(j)
|
||||
}
|
||||
|
||||
// due to go not having any other loop for with no condition == while
|
||||
for {
|
||||
fmt.Println("loop")
|
||||
break
|
||||
}
|
||||
|
||||
//continue passes to the next iter of the loop
|
||||
for n := 0; n <= 5; n++ {
|
||||
if n%2 == 0 {
|
||||
continue
|
||||
}
|
||||
fmt.Println(n)
|
||||
}
|
||||
}
|
||||
BIN
functions/functions
Executable file
BIN
functions/functions
Executable file
Binary file not shown.
25
functions/functions.go
Normal file
25
functions/functions.go
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// here is a function that takes two ints and sums them
|
||||
// then returns them as an int
|
||||
// go requires explicit returns meaning it wont
|
||||
// automatically return the value of the last expression
|
||||
func plus(a int,b int) int {
|
||||
return a+b
|
||||
}
|
||||
|
||||
//when you have multiple consecutive params with the same type they can be type declared as such:
|
||||
func plusPlus(a,b,c int) int {
|
||||
return a+b+c
|
||||
}
|
||||
|
||||
//call functions like you would expect
|
||||
func main(){
|
||||
res := plus(1,2)
|
||||
fmt.Println("1+2=",res)
|
||||
|
||||
res = plusPlus(1,2,3)
|
||||
fmt.Println("1+2+3=",res)
|
||||
}
|
||||
BIN
generics/generics
Executable file
BIN
generics/generics
Executable file
Binary file not shown.
66
generics/generics.go
Normal file
66
generics/generics.go
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
//as an example of a generic function,
|
||||
//MapKeys takes a map of any type and returns a slice of its keys.
|
||||
//this function has two type parameters - K and V; K has the comparable constraint
|
||||
//meaning that we can compare values of this type with the == and != operators
|
||||
//this is required for map keys in Go V has the any constraint meaning that its
|
||||
// not restricted in any way(any is an alias for interface{})
|
||||
func MapKeys[K comparable, V any](m map [K]V) []K {
|
||||
r := make([]K,0,len(m))
|
||||
for k := range m {
|
||||
r = append(r,k)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
|
||||
//as example of a generic type, List is a singly-linked list with values of any type
|
||||
type List[T any] struct {
|
||||
head, tail *element[T]
|
||||
}
|
||||
|
||||
type element[T any] struct {
|
||||
next *element[T]
|
||||
val T
|
||||
}
|
||||
|
||||
//We can define methods on generic types just like we do on regular types,
|
||||
// but we have to keep the type parameters in place.
|
||||
// the type is List[T] not List
|
||||
func (lst *List[T]) Push(v T) {
|
||||
if lst.tail == nil {
|
||||
lst.head = &element[T]{val:v}
|
||||
lst.tail = lst.head
|
||||
} else {
|
||||
lst.tail.next = &element[T]{val: v}
|
||||
lst.tail = lst.tail.next
|
||||
}
|
||||
}
|
||||
|
||||
func (lst *List[T]) GetAll() []T {
|
||||
var elems []T
|
||||
for e := lst.head; e != nil; e = e.next {
|
||||
elems = append(elems, e.val)
|
||||
}
|
||||
return elems
|
||||
}
|
||||
|
||||
func main() {
|
||||
var m = map[int]string{1: "2",2:"4",4:"8"}
|
||||
|
||||
//When invoking genric functions, we can often
|
||||
// rely on type inference. note that we dont have to specify
|
||||
// the types for K and V when calling MapKeys - the compiler infers them automatically
|
||||
fmt.Println("keys:",MapKeys(m))
|
||||
//though we could be explicit
|
||||
_ = MapKeys[int,string](m)
|
||||
|
||||
lst := List[int]{}
|
||||
lst.Push(10)
|
||||
lst.Push(13)
|
||||
lst.Push(23)
|
||||
fmt.Println("list:",lst.GetAll())
|
||||
}
|
||||
BIN
goroutines/goroutines
Executable file
BIN
goroutines/goroutines
Executable file
Binary file not shown.
33
goroutines/goroutines.go
Normal file
33
goroutines/goroutines.go
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func f (from string){
|
||||
for i := 0; i<3; i++ {
|
||||
fmt.Println(from,":",i)
|
||||
}
|
||||
}
|
||||
|
||||
func main(){
|
||||
//suppose we have function call f(s)
|
||||
//here is how wed call that in the usual way
|
||||
//running it synchronously
|
||||
f("direct")
|
||||
|
||||
//To invoke this function in a goroutine, use go f(s)
|
||||
//This new goroutine will execute concurrently with the calling one
|
||||
go f("goroutine")
|
||||
|
||||
//you can also start a goroutine for an anonymous function call
|
||||
go func (msg string) {
|
||||
fmt.Println(msg)
|
||||
}("going")
|
||||
|
||||
//our two function calls are running async in seperate goroutines now.
|
||||
//wait for them to finish (for a most robust approach use a WaitGroup)
|
||||
time.Sleep(time.Second)
|
||||
fmt.Println("done")
|
||||
}
|
||||
BIN
hello-world/hello-world
Executable file
BIN
hello-world/hello-world
Executable file
Binary file not shown.
5
hello-world/hello-world.go
Normal file
5
hello-world/hello-world.go
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
package main
|
||||
import "fmt"
|
||||
func main(){
|
||||
fmt.Println("hello world")
|
||||
}
|
||||
BIN
if-else/if-else
Executable file
BIN
if-else/if-else
Executable file
Binary file not shown.
35
if-else/if-else.go
Normal file
35
if-else/if-else.go
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main(){
|
||||
|
||||
//There are no ternary ifs in go
|
||||
|
||||
//basic if else
|
||||
if 7%2 == 0 {
|
||||
fmt.Println("7 is even")
|
||||
} else {
|
||||
fmt.Println("7 is odd")
|
||||
}
|
||||
//if doesnt need else
|
||||
if 8%4 == 0 {
|
||||
fmt.Println("8 is divisible by 4")
|
||||
}
|
||||
|
||||
//You can use logical operators in if such as && and ||
|
||||
if 7%2 == 0 || 8%2 == 0 {
|
||||
fmt.Println("either 8 or 7 are even")
|
||||
}
|
||||
|
||||
//a statement can precede conditionals;
|
||||
// any variables declared in the statement
|
||||
//are avalible in the current and all subsequent branches.
|
||||
if num := 9; num < 0 {
|
||||
fmt.Println(num, "is negative")
|
||||
} else if num < 10 {
|
||||
fmt.Println(num, "has 1 digit")
|
||||
} else {
|
||||
fmt.Println(num, "has multiple digits")
|
||||
}
|
||||
}
|
||||
BIN
interfaces/interfaces
Executable file
BIN
interfaces/interfaces
Executable file
Binary file not shown.
54
interfaces/interfaces.go
Normal file
54
interfaces/interfaces.go
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
)
|
||||
// here is a basic interface for geometric shapes
|
||||
type geometry interface {
|
||||
area() float64
|
||||
perim() float64
|
||||
}
|
||||
|
||||
//for our example well implement this interface on rect and circle types
|
||||
type rect struct {
|
||||
width,height float64
|
||||
}
|
||||
type circle struct {
|
||||
radius float64
|
||||
}
|
||||
|
||||
// to implement an interface in Go,
|
||||
//we just need to implement all the methods in the interface
|
||||
//here we do it for rectangles
|
||||
func (r rect) area() float64 {
|
||||
return r.width * r.height
|
||||
}
|
||||
func (r rect) perim() float64 {
|
||||
return 2*r.width + 2*r.height
|
||||
}
|
||||
//here we do it for circles
|
||||
func (c circle) area() float64 {
|
||||
return math.Pi * c.radius * c.radius
|
||||
}
|
||||
func (c circle) perim() float64 {
|
||||
return 2*math.Pi*c.radius
|
||||
}
|
||||
|
||||
//if a variable has an interface type,
|
||||
// then we can call methods that are in the named interface
|
||||
//here's a generic measure function taking advantage of this
|
||||
//to work on any geometry
|
||||
func measure(g geometry) {
|
||||
fmt.Println(g)
|
||||
fmt.Println(g.area())
|
||||
fmt.Println(g.perim())
|
||||
}
|
||||
|
||||
func main(){
|
||||
r := rect{width: 3,height: 4}
|
||||
c := circle{radius:5}
|
||||
|
||||
measure(r)
|
||||
measure(c)
|
||||
}
|
||||
BIN
maps/maps
Executable file
BIN
maps/maps
Executable file
Binary file not shown.
57
maps/maps.go
Normal file
57
maps/maps.go
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"maps"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// to create an empty map, use the builtin make:
|
||||
// make(map[key-type]val-type)
|
||||
m := make(map[string]int)
|
||||
|
||||
//set key/value pairs using typical name[key] = val syntax
|
||||
m["k1"] = 7
|
||||
m["k2"] = 13
|
||||
|
||||
// printing a map with e.g.
|
||||
// fmt.Println will show all of its key/value pairs
|
||||
fmt.Println("map:",m)
|
||||
|
||||
// Get a value from a key with name[key]
|
||||
// if the key doesn't exist the zero value of the value type is returned
|
||||
|
||||
v1 := m["k1"]
|
||||
fmt.Println("v1:",v1)
|
||||
|
||||
v3 := m["k3"]
|
||||
fmt.Println("v3:",v3)
|
||||
|
||||
//the builtin len returns the number of key/value pairs when called on a map
|
||||
fmt.Println("len:", len(m))
|
||||
|
||||
//the builtin delete removes key/value pairs from a map,
|
||||
//use the clear builtin to remove all key/value pairs from a map.
|
||||
delete(m, "k2")
|
||||
fmt.Println("map:",m)
|
||||
|
||||
clear(m)
|
||||
fmt.Println("map:",m)
|
||||
|
||||
// the optional second return value when getting the value
|
||||
// from a map indicates if the key was present in the map allowing
|
||||
// to diferentiate between default zero-values and values acutally stored as 0
|
||||
// here we dont need the value itself so we ignore it with the _ or blank identifier
|
||||
_, prs := m["k2"]
|
||||
fmt.Println("prs:",prs)
|
||||
|
||||
//you can declare and initalize a new map in the same line with the following syntax:
|
||||
n := map[string]int{"foo": 1,"bar": 2}
|
||||
fmt.Println("map:", n)
|
||||
|
||||
//the map package also contains useful funcs for map such as .Equal
|
||||
n2 := map[string]int{"foo": 1,"bar":2}
|
||||
if maps.Equal(n,n2){
|
||||
fmt.Println("n == n2")
|
||||
}
|
||||
}
|
||||
BIN
methods/methods
Executable file
BIN
methods/methods
Executable file
Binary file not shown.
34
methods/methods.go
Normal file
34
methods/methods.go
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
type rect struct {
|
||||
width, height int
|
||||
}
|
||||
|
||||
//this area method has a reciever type of *rect
|
||||
func (r *rect) area() int {
|
||||
return r.width * r.height
|
||||
}
|
||||
|
||||
// Methods can be defined for either pointer or value reciever types
|
||||
// heres an example of a value receiver
|
||||
func (r rect) perim() int {
|
||||
return 2*r.width + 2*r.height
|
||||
}
|
||||
|
||||
func main() {
|
||||
r := rect{width:10,height:5}
|
||||
|
||||
//here we call the two methods defined for our struct
|
||||
fmt.Println("area:",r.area())
|
||||
fmt.Println("perim:",r.perim())
|
||||
|
||||
//Go automatically handles conversion between values and pointers for method calls.
|
||||
//you may want to use a pointer receiver type to avoid copying on method calls or to allow
|
||||
//the method to mutate the receiving struct
|
||||
|
||||
rp := &r
|
||||
fmt.Println("area:",rp.area())
|
||||
fmt.Println("perim:",rp.perim())
|
||||
}
|
||||
BIN
multi-return-values/mrv
Executable file
BIN
multi-return-values/mrv
Executable file
Binary file not shown.
18
multi-return-values/mrv.go
Normal file
18
multi-return-values/mrv.go
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
//the (int,int) in the func sig shows that the func returns 2 ints
|
||||
func vals()(int,int){
|
||||
return 3,7
|
||||
}
|
||||
|
||||
func main(){
|
||||
//here we use the 2 different values from the call with multiple assignment
|
||||
a,b := vals()
|
||||
fmt.Println(a)
|
||||
fmt.Println(b)
|
||||
//if you only want a subset of the returned values use the blank identifier
|
||||
_,c := vals()
|
||||
fmt.Println(c)
|
||||
}
|
||||
BIN
pointers/pointers
Executable file
BIN
pointers/pointers
Executable file
Binary file not shown.
37
pointers/pointers.go
Normal file
37
pointers/pointers.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// go supports pointers allowing you to pass refs to values and records
|
||||
// in your program
|
||||
|
||||
|
||||
//this func takes in an int as a param
|
||||
func zeroval(ival int){
|
||||
ival = 0
|
||||
}
|
||||
|
||||
//this func takes in an pointer to an int as a parameter
|
||||
func zeroptr (iptr *int) {
|
||||
*iptr = 0
|
||||
}
|
||||
|
||||
func main() {
|
||||
//display initial value of i
|
||||
i := 1
|
||||
fmt.Println("initial:",i)
|
||||
|
||||
//display the value of i after passing to zeroval
|
||||
zeroval(i)
|
||||
fmt.Println("zeroval:",i)
|
||||
|
||||
//display the value of the data @ the memory address of i aka i's pointer
|
||||
zeroptr(&i)
|
||||
fmt.Println("zeroptr:",i)
|
||||
|
||||
//pointers can be printed as well and will return their memory address
|
||||
fmt.Println("pointer:",&i)
|
||||
|
||||
//zeroval doesnt change the val of i but zeroptr does because it edits the data at the memory address
|
||||
|
||||
}
|
||||
BIN
range/range
Executable file
BIN
range/range
Executable file
Binary file not shown.
37
range/range.go
Normal file
37
range/range.go
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main(){
|
||||
//here we use range to sum the numbers in a slice
|
||||
nums := []int{2,3,4}
|
||||
sum := 0
|
||||
for _,num := range nums {
|
||||
sum += num
|
||||
}
|
||||
fmt.Println("sum:",sum)
|
||||
|
||||
//range on arrays and slices provides both the index and value
|
||||
// at each entry. above we didn't need the index, so we ignored it with the blank identifier
|
||||
// but if we want the index we can use it like bellow
|
||||
for i, num := range nums {
|
||||
if num == 3 {
|
||||
fmt.Println("index:",i)
|
||||
}
|
||||
}
|
||||
//range on map iterates over the key value pairs.
|
||||
kvs := map[string]string{"a":"apple","b":"banana"}
|
||||
for k,v := range kvs{
|
||||
fmt.Printf("%s -> %s\n",k,v)
|
||||
}
|
||||
//range can also iterate over just the keys of a map
|
||||
for k := range kvs{
|
||||
fmt.Println("key:",k)
|
||||
}
|
||||
// range on strings iterates over Unicode code points.
|
||||
// the first value is the starting byte index of the rune
|
||||
//and the second is the rune itself.
|
||||
for i,c := range "go"{
|
||||
fmt.Println(i,c)
|
||||
}
|
||||
}
|
||||
BIN
recursion/recursion
Executable file
BIN
recursion/recursion
Executable file
Binary file not shown.
30
recursion/recursion.go
Normal file
30
recursion/recursion.go
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
//this calls itself until it reaches the base case of fact(0)
|
||||
func fact(n int) int {
|
||||
if n == 0 {
|
||||
return 1
|
||||
}
|
||||
return n * fact(n-1)
|
||||
}
|
||||
|
||||
func main() {
|
||||
fmt.Println(fact(7))
|
||||
|
||||
// closures can also be recursive,
|
||||
// but this requires the closure to be declared with
|
||||
// a typed var explicitly before its defined
|
||||
var fib func(n int) int
|
||||
|
||||
fib = func(n int) int {
|
||||
if n < 2 {
|
||||
return n
|
||||
}
|
||||
return fib(n-1) + fib(n-2)
|
||||
}
|
||||
|
||||
//simce fib was previously declared in main Go knows which function to call with fib here
|
||||
fmt.Println(fib(7))
|
||||
}
|
||||
BIN
select/select
Executable file
BIN
select/select
Executable file
Binary file not shown.
39
select/select.go
Normal file
39
select/select.go
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
//Go's select lets you wait on multiple channel
|
||||
//operations combining goroutines and channels with select
|
||||
//is a powerful feature of go
|
||||
func main(){
|
||||
//For our example well 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 e.g. blocking RPC
|
||||
//operations executing in concurrent goroutines
|
||||
go func(){
|
||||
time.Sleep(1*time.Second)
|
||||
c1 <- "one"
|
||||
}()
|
||||
go func(){
|
||||
time.Sleep(2*time.Second)
|
||||
c2 <- "two"
|
||||
}()
|
||||
|
||||
|
||||
//we'll use select to await both of these values
|
||||
//simultaneously, printing each one as it arrives.
|
||||
for i := 0; i < 2; i++ {
|
||||
select{
|
||||
case msg1 := <-c1:
|
||||
fmt.Println("received", msg1)
|
||||
case msg2 := <-c2:
|
||||
fmt.Println("received", msg2)
|
||||
}
|
||||
}
|
||||
}
|
||||
BIN
slices/slices
Executable file
BIN
slices/slices
Executable file
Binary file not shown.
76
slices/slices.go
Normal file
76
slices/slices.go
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
)
|
||||
|
||||
func main(){
|
||||
// Unlike arrays, slices are typed only by the elements they contain.
|
||||
// not the number of elements.
|
||||
// an uninitalized slice equals to nil and has a len of 0
|
||||
var s []string
|
||||
fmt.Println("uninit:", s, s == nil, len(s) == 0)
|
||||
|
||||
// to create an emply slice with non-zero length,
|
||||
// use the builtin make. here we make a slice of strings of length 3 (initally zero-valued)
|
||||
// by default a new slice's capacity == its len if we know the slice is going to grow ahead of time
|
||||
// its possible to pass a capacity explicitly as an additional parameter to make.
|
||||
s = make([]string, 3)
|
||||
fmt.Println("emp:", s, len(s), "cap:", cap(s))
|
||||
|
||||
//setting and getting vars works just like arrays
|
||||
s[0] = "a"
|
||||
s[1] = "b"
|
||||
s[2] = "c"
|
||||
fmt.Println("set:", s)
|
||||
fmt.Println("get:", s[2])
|
||||
// len reutrns the length of the slice as expected.
|
||||
fmt.Println("len:", len(s))
|
||||
// in addition to these basic opertaions,
|
||||
// slices support several more than make them richer than arrays.
|
||||
// One is the builtin append which returns a slice containing
|
||||
// one or more new values. Note that we need to accept a return value
|
||||
//from append as we make get a new slice value.
|
||||
s = append(s, "")
|
||||
s = append(s, "e", "f")
|
||||
|
||||
// slices can be copy'd
|
||||
// below we create an empty slice c
|
||||
// of the same len as s and copy into c from s
|
||||
c := make([]string, len(s))
|
||||
copy(c,s)
|
||||
fmt.Println("cpy:",c)
|
||||
|
||||
//slices also support a slice operator with the syntax slice[low:high].
|
||||
// for example this gets a slice of the elements s[2],s[3],s[4]
|
||||
l := s[2:5]
|
||||
fmt.Println("sl1", l)
|
||||
|
||||
l = s[:5]
|
||||
fmt.Println("sl2", l)
|
||||
|
||||
l = s[2:]
|
||||
fmt.Println("sl3", l)
|
||||
|
||||
//single line declaration and initalization of a slice
|
||||
t := []string{"g","h","i"}
|
||||
|
||||
//the slices package has plenty of useful funcs for slices like .Equal
|
||||
t2 := []string{"g","h","i"}
|
||||
if slices.Equal(t,t2){
|
||||
fmt.Println("t == t2")
|
||||
}
|
||||
|
||||
// Slices can be composed into multi-dimensional data structues,
|
||||
// the length of the inner slices can vary, unlike with multi-dimensional arrays.
|
||||
twoD := make([][]int, 3)
|
||||
for i := 0; i < 3; i ++ {
|
||||
innerLen := i + 1
|
||||
twoD[i] = make([]int, innerLen)
|
||||
for j := 0; j < innerLen; j++ {
|
||||
twoD[i][j] = i + j
|
||||
}
|
||||
}
|
||||
fmt.Println("2d: ", twoD)
|
||||
}
|
||||
BIN
strings-and-runes/stringsandrunes
Executable file
BIN
strings-and-runes/stringsandrunes
Executable file
Binary file not shown.
55
strings-and-runes/stringsandrunes.go
Normal file
55
strings-and-runes/stringsandrunes.go
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// s is a string assigned a literal value representing the
|
||||
// word "hello" in the Thai language.
|
||||
// Go string literals are UTF-8 encoded text.
|
||||
const s = "สวัสดี"
|
||||
//Since strings are equivalent to []byte,
|
||||
//this will produce the length of the raw bytes stored within
|
||||
fmt.Println("len:", len(s))
|
||||
|
||||
// indexing into a string produces the raw byte values at each index.
|
||||
// This loop generates the hex values of all the bytes
|
||||
// that constitute the code points in s.
|
||||
for i := 0; i < len(s); i++ {
|
||||
fmt.Printf("%x ", s[i])
|
||||
}
|
||||
fmt.Println()
|
||||
// To count how many runes are in a string,
|
||||
// we ca can use the utf8 package. Note that
|
||||
// the run-time of RuneCountInString depends on the size of the string
|
||||
// due to having to decode each rune sequentially.
|
||||
// Some Thai characters are represented by multiple UTF-8 code points,
|
||||
// so the result of this count may be suprising.
|
||||
fmt.Println("Rune count:",utf8.RuneCountInString(s))
|
||||
// A range loop handles strings specially and decodes each rune
|
||||
// along with it's offset in the string
|
||||
for idx, runeValue := range s {
|
||||
fmt.Printf("%#U Starts at %d\n", runeValue, idx)
|
||||
}
|
||||
//we can achieve the same iteration by using the utf8.DecodeRuneInString function explicitly
|
||||
fmt.Println("\nUsing DecodeRuneInString")
|
||||
for i,w := 0,0; i < len(s); i += w {
|
||||
runeValue, width := utf8.DecodeRuneInString(s[i:])
|
||||
fmt.Printf("%#U starts at %d\n", runeValue, i)
|
||||
w = width
|
||||
// This demonstrates passing a rune to a func
|
||||
examineRune(runeValue)
|
||||
}
|
||||
}
|
||||
|
||||
// Values enclosed in single quotes are rune literals.
|
||||
// we can compare a rune value to a rune literal directly.
|
||||
func examineRune(r rune) {
|
||||
if r == 't' {
|
||||
fmt.Println("found tee")
|
||||
} else if r == 'ส' {
|
||||
fmt.Println("found so sua")
|
||||
}
|
||||
}
|
||||
BIN
struct-embedding/structembed
Executable file
BIN
struct-embedding/structembed
Executable file
Binary file not shown.
54
struct-embedding/structembed.go
Normal file
54
struct-embedding/structembed.go
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
|
||||
//Go struct embedding allows for structs to be embedded into interfaces
|
||||
// to express a more seamless composition of types.
|
||||
type base struct {
|
||||
num int
|
||||
}
|
||||
|
||||
func (b base) describe() string{
|
||||
return fmt.Sprintf("base with num=%v",b.num)
|
||||
}
|
||||
|
||||
//a container embeds a base
|
||||
//an embedding looks like a field without a name
|
||||
type container struct {
|
||||
base
|
||||
str string
|
||||
}
|
||||
|
||||
|
||||
func main(){
|
||||
|
||||
//when creating structs with literals, we have to
|
||||
//initalize the embedding explicitly;
|
||||
// here the embedded types serve as the field name
|
||||
co := container{
|
||||
base: base {
|
||||
num: 1,
|
||||
},
|
||||
str: "some name",
|
||||
}
|
||||
|
||||
//we can access the base's fields directly on co, e.g. co.num
|
||||
fmt.Printf("co={num: %v str: %v}\n", co.num, co.str)
|
||||
|
||||
//alternativly we can spell out the full path using the embedded type name
|
||||
fmt.Println("also num:", co.base.num)
|
||||
//since container embeds base, the methods of base also become the methods of container
|
||||
//here we invoke a method that was embedded from base directly on co
|
||||
fmt.Println("describe:", co.describe())
|
||||
|
||||
//Embedding structs with methods may be used
|
||||
// to bestow interface implementations onto other structs.
|
||||
// here we see that container now implements the describer interface because it
|
||||
// embeds base
|
||||
type describer interface {
|
||||
describe() string
|
||||
}
|
||||
var d describer = co
|
||||
fmt.Println("describer:", d.describe())
|
||||
}
|
||||
BIN
structs/structs
Executable file
BIN
structs/structs
Executable file
Binary file not shown.
58
structs/structs.go
Normal file
58
structs/structs.go
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
// Structs are typed collections of fields.
|
||||
// they're useful for grouping data together to form records
|
||||
// the person struct type has name and age fields
|
||||
type person struct {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
// newPerson constructs a new person struct with the given name
|
||||
// you can safely return a pointer to local variable as a local variable
|
||||
// will survive the scope of the func
|
||||
func newPerson(name string) *person {
|
||||
p := person{name: name}
|
||||
p.age = 42
|
||||
return &p
|
||||
}
|
||||
|
||||
func main(){
|
||||
// This syntax creates a new struct
|
||||
fmt.Println(person{"Bob", 20})
|
||||
//you can name the fields when initalizing the struct
|
||||
fmt.Println(person{name: "Alice", age: 30})
|
||||
//omitted fields will be zero-valued
|
||||
fmt.Println(person{name:"Fred"})
|
||||
//an & prefix yields a pointer to the struct
|
||||
fmt.Println(&person{name:"Ann",age: 40})
|
||||
//its idiomatic to encapsulate new struct creation in constructor functions
|
||||
fmt.Println(newPerson("Jon"))
|
||||
|
||||
//access struct fields with . notation
|
||||
s := person{name: "Sean", age: 50}
|
||||
fmt.Println(s.name)
|
||||
|
||||
// you can also use . notation with struct pointers - the pointers are
|
||||
// automatically dereferenced
|
||||
sp := &s
|
||||
fmt.Println(sp.age)
|
||||
|
||||
// structs are mutable
|
||||
sp.age = 51
|
||||
fmt.Println(sp.age)
|
||||
|
||||
// if a struct type is only used for a single value
|
||||
// we dont have to give it a name
|
||||
// the value can have an anonymous struct type.
|
||||
// this tech is used commonly in table driven tests
|
||||
dog := struct {
|
||||
name string
|
||||
isGood bool
|
||||
}{
|
||||
"Rex",
|
||||
true,
|
||||
}
|
||||
fmt.Println(dog)
|
||||
}
|
||||
BIN
switch/switch
Executable file
BIN
switch/switch
Executable file
Binary file not shown.
54
switch/switch.go
Normal file
54
switch/switch.go
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
// Basic switch default is optional
|
||||
i := 2
|
||||
fmt.Print("Write", i, " as ")
|
||||
switch i {
|
||||
case 1:
|
||||
fmt.Println("one")
|
||||
case 2:
|
||||
fmt.Println("two")
|
||||
case 3:
|
||||
fmt.Println("three")
|
||||
}
|
||||
|
||||
// Commas can seperate multiple expressions in the same case statement.
|
||||
switch time.Now().Weekday() {
|
||||
case time.Saturday, time.Sunday:
|
||||
fmt.Println("It's the weekend")
|
||||
default:
|
||||
fmt.Println("It's a weekday")
|
||||
}
|
||||
|
||||
//switch without an expression is a alternate way to express if/else logic
|
||||
t := time.Now()
|
||||
switch {
|
||||
case t.Hour() < 12:
|
||||
fmt.Println("It's before noon")
|
||||
default:
|
||||
fmt.Println("It's afte noon")
|
||||
}
|
||||
|
||||
// a type switch compares types instead of values.
|
||||
//you can use this to discorver the type of an interface value.
|
||||
whatAmI := func(i interface{}){
|
||||
switch t := i.(type) {
|
||||
case bool:
|
||||
fmt.Println("I'm a bool")
|
||||
case int:
|
||||
fmt.Println("I'm an int")
|
||||
default:
|
||||
fmt.Printf("Don't know the type %T\n",t)
|
||||
}
|
||||
}
|
||||
whatAmI(true)
|
||||
whatAmI(1)
|
||||
whatAmI("hey")
|
||||
}
|
||||
10
timeouts/timeouts.go
Normal file
10
timeouts/timeouts.go
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
}
|
||||
BIN
values/values
Executable file
BIN
values/values
Executable file
Binary file not shown.
20
values/values.go
Normal file
20
values/values.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main(){
|
||||
// Strings which can be concatinated by +
|
||||
fmt.Println("go"+"lang")
|
||||
// Strings can be iterpolated with a result
|
||||
// of an expression using fmt.Println and
|
||||
// seperating the expression and string by a ,
|
||||
// in this case the string is interpolated with
|
||||
// a sum of ints and sum of floats
|
||||
fmt.Println("1+1=",1+1)
|
||||
fmt.Println("7.0/3.0=",7.0/3.0)
|
||||
|
||||
// Booleans and boolean operators
|
||||
fmt.Println(true && false)
|
||||
fmt.Println(true || false)
|
||||
fmt.Println(!true)
|
||||
}
|
||||
BIN
variables/variables
Executable file
BIN
variables/variables
Executable file
Binary file not shown.
27
variables/variables.go
Normal file
27
variables/variables.go
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
package main
|
||||
import "fmt"
|
||||
|
||||
func main(){
|
||||
// var declares 1 or more variables.
|
||||
var a = "initial"
|
||||
fmt.Println(a)
|
||||
// You can declare multiple variables at once.
|
||||
var b,c int = 1,2
|
||||
fmt.Println(b,c)
|
||||
|
||||
// Go will infer the type of initialized variables.
|
||||
var d = true
|
||||
fmt.Println(d)
|
||||
|
||||
// Variables declared without a corresponding
|
||||
// initialization are zero-valued. For example,
|
||||
// the zero value for an int is 0.
|
||||
var e int
|
||||
fmt.Println(e)
|
||||
// The := syntax is shorthand for declaring and
|
||||
// initializing a variable, e.g.
|
||||
// for var f string = "apple" in this case.
|
||||
// This syntax is only available inside functions.
|
||||
f := "apple"
|
||||
fmt.Println(f)
|
||||
}
|
||||
BIN
variadic-functions/vfunc
Executable file
BIN
variadic-functions/vfunc
Executable file
Binary file not shown.
20
variadic-functions/vfunc.go
Normal file
20
variadic-functions/vfunc.go
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
// below is a func that takes in an arbitrary number of ints as args
|
||||
func sum(nums ...int) {
|
||||
fmt.Print(nums, " ")
|
||||
total := 0
|
||||
for _, num := range nums {
|
||||
total += num
|
||||
}
|
||||
fmt.Println(total)
|
||||
}
|
||||
|
||||
func main(){
|
||||
sum(1,2)
|
||||
sum(1,2,3)
|
||||
nums := []int{1,2,3,4}
|
||||
sum(nums...)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue