Введение
В целях систематизации знаний по Go, решил завести данный пост. Информация представлена в виде небольших программ и их разбора. Тиакие задачи могут вам встретиться на собеседованиях. Я планирую дополнять его по мере поступления новых задач.
Вопросы
Почему программа отработала не правильно, и как это исправить.
package main
import (
"fmt"
)
type Person struct {
FirstName string
LastName string
}
func (p Person) Married(husband Person) {
p.LastName = husband.LastName
}
func main() {
eva := Person{"Eva", "First"}
adam := Person{"Adam", "Second"}
eva.Married(adam)
fmt.Println(eva)
}
В Go можно передавать параметры в функцию по ссылке и по значению. Если параметр предается по значению(как в нашем примере), то все параметры копируются в другие адреса памяти и работа внутри функции происходит с ними, поэтому ожидаемой смены фамилии не происходит. Если же параметр функции передается по ссылке, создается новая ссылка на существующую область памяти и, соответственно, при изменении меняется и то значение которое находится по ссылке.
Для ожидаемого поведения нужно изменить объявление функции: вместо
func (p Person) Married(husband Person)
написать
func (p *Person) Married(husband Person)
Таким образом мы передадим функции параметр не по значению, а по ссылке, что и нужно для правильной работы.
Почему программа работает не правильно, и как это исправить.
package main
import "fmt"
func main() {
var actions []func()
for _, dir := range [...]string{"one", "two", "three", "four"} {
actions = append(actions, func() {
fmt.Println(dir)
})
}
for _, f := range actions {
f()
}
}
В данном примере происходит отложенный вызов функций, которые будут ссылаться на одну и туже область памяти dir
, которая перезаписывается при каждой интерации в цикле.
Для правильной работы нужно изменить тело цикла следующим образом: вместо
actions = append(actions, func() {
fmt.Println(dir)
})
вставить
a := dir
actions = append(actions, func() {
fmt.Println(a)
})
Таким образом при каждой итерауции будет инициализироваться новая область памяти, в которой будет значение dir
.
Почему программа работает, не как ожидается и как это исправить:
package main
import (
"fmt"
)
func showNumber(num int) {
fmt.Println(num)
}
func main() {
iterations := 10
for i := 0; i <= iterations; i++ {
go showNumber(i)
}
fmt.Println("Goodbye!")
}
Проблема данного кода в том, что горутины не успевают отработать, так как при выходе из программы все горутины убиваются.
Самый простой вариант добавить time.Sleep
для паузы при завершении. Более правильный вариант это использование пакета sync.WaitGroup
.
Код будет следующий:
package main
import (
"fmt"
"sync"
)
func showNumber(num int) {
fmt.Println(num)
}
func main() {
var wg sync.WaitGroup
iterations := 10
for i := 0; i <= iterations; i++ {
wg.Add(1)
go showNumber(i)
}
wg.Wait()
fmt.Println("Goodbye!")
}
Мы создаем группу ожидающих горутин и ждем когда каждая из них пришлет сигнал о завершении.
Почему не выводится строка “do is ok”:
package main
import (
"fmt"
)
type MyError struct {
Reason string
}
func (e *MyError) Error() string {
return e.Reason
}
func do() error {
var err *MyError
fmt.Println(err == nil)
return err
}
func main() {
if err := do(); err == nil {
fmt.Println("do is ok")
}
}
Интерфейс в Go состоит из 2-х элементов тип и значение. Функция do
будет возвращать интерфейс не с типом nil
, а с типом *MyError
, т.е. (*MyError, nil)``, что не является nil, так как значение интерфейса равно
nilтолько в том случае, если оба элемента равны
nil, т. е.
(nil, nil). Следовательно
(*MyError, nil) != (nil, nil)```.