Lançado · em melhoria
Guia de Go · 3/6
Por enquanto, este capítulo está disponível apenas em inglês.
Go is statically typed, but type inference keeps it from feeling verbose. This chapter tours the data building blocks of every Go program.
| Kind | Types |
|---|---|
| Boolean | bool |
| Integers | int, int8 to int64, uint, uint8 to uint64 |
| Floating point and complex | float32, float64, complex64, complex128 |
| Strings | string (an immutable sequence of UTF-8 bytes) |
| Aliases | byte (same as uint8), rune (same as int32, a Unicode code point) |
Go never converts between numeric types implicitly. To add an int to a float64 you write float64(n) explicitly. Strings are bytes under the hood, but ranging over a string decodes it into runes, which is what you want for text.
n := 3
f := 1.5
total := float64(n) + f
s := "héllo"
fmt.Println(len(s)) // 6: length in bytes, not characters
for i, r := range s {
fmt.Println(i, string(r)) // the é occupies bytes 1 and 2
}An array has a fixed length that is part of its type, so [3]int and [4]int are different types. In practice you will reach for slices far more often. A slice is a lightweight view onto an underlying array, with a length (len) and a capacity (cap). When append runs out of capacity it allocates a bigger array, which is why you must always assign its result back.
arr := [3]int{1, 2, 3} // array
nums := []int{1, 2, 3} // slice
nums = append(nums, 4, 5)
part := nums[1:3] // [2 3], shares memory with nums
part[0] = 20 // nums[1] is now 20 as well
buf := make([]byte, 0, 64) // length 0, capacity 64
copied := make([]int, len(nums))
copy(copied, nums) // an independent copy
fmt.Println(arr, nums, len(buf), cap(buf), copied)Because sub-slices share storage with the original, use copy or slices.Clone when you need an independent value. The standard slices package also provides everyday helpers such as slices.Sort, slices.Contains, and slices.Index.
A map is a built-in hash table. Reading a missing key returns the zero value of the value type, so when presence matters, use the two-value form and check the second result, conventionally named . Iteration order over a map is deliberately unspecified.
okages := map[string]int{"alice": 31, "bob": 27}
ages["carol"] = 40
if age, ok := ages["dave"]; !ok {
fmt.Println("no dave", age) // age is 0
}
delete(ages, "bob")
for name, age := range ages {
fmt.Println(name, age)
}
counts := make(map[string]int)
counts["go"]++ // missing keys start at zero, so counting just worksA nil map can be read from but panics on write, so create maps with a literal or with make.
A struct groups named fields. Go has no classes; instead you attach methods to types. A method is simply a function with a receiver. Use a pointer receiver (*T) when the method mutates the value or the struct is large, and a value receiver for small read-only types. Within a single type, try to stick to one kind of receiver.
type Point struct {
X, Y float64
}
func (p Point) Dist() float64 { // value receiver: works on a copy
return math.Hypot(p.X, p.Y)
}
func (p *Point) Scale(k float64) { // pointer receiver: mutates the original
p.X *= k
p.Y *= k
}
type Named struct {
Point // embedded: Point's fields and methods are promoted
Name string
}
func main() {
p := Point{X: 3, Y: 4}
p.Scale(2) // Go takes &p for you
n := Named{Point: p, Name: "A"}
fmt.Println(p.Dist(), n.X, n.Dist())
}Instead of inheritance, Go offers embedding: placing one type inside another promotes its fields and methods.
An interface is a set of method signatures. There is no implements keyword: any type that has the right methods satisfies the interface automatically. As a result, interfaces tend to be small and defined by the code that uses them.
type Shape interface {
Area() float64
}
type Rect struct{ W, H float64 }
type Circle struct{ R float64 }
func (r Rect) Area() float64 { return r.W * r.H }
func (c Circle) Area() float64 { return math.Pi * c.R * c.R }
func describe(v any) string {
switch x := v.(type) { // type switch
case Shape:
return fmt.Sprintf("area %.2f", x.Area())
case string:
return "a string: " + x
default:
return "unknown"
}
}
func main() {
shapes := []Shape{Rect{2, 3}, Circle{1}}
for _, s := range shapes {
fmt.Println(describe(s))
}
}any is an alias for the empty interface interface{} and can hold a value of any type. To get the concrete value back out, use a type assertion v.(T) or a type switch. Go also supports generics through type parameters, so an algorithm that works over many types can be written once, for example func Max[T cmp.Ordered](a, b T) T.
A pointer holds the address of a value: &x takes the address, and *p reads or writes through it. There is no pointer arithmetic, and memory is garbage collected, so returning the address of a local variable is perfectly safe.
func newCounter() *int {
c := 0
return &c // safe: c escapes to the heap
}
func main() {
p := newCounter()
*p++
fmt.Println(*p) // 1
pt := &Point{1, 2}
pt.X = 10 // no need to write (*pt).X
}Go always passes arguments by value. Reach for a pointer when a function must modify its argument or when copying a large struct would be wasteful. Slices and maps already contain references internally, so copying one still points at the same underlying data.
range yields runes.append.v, ok := m[k].
0 comentários
Fazer login · Faça login para deixar um comentário.
Seja o primeiro a comentar.