Logo
Книга для начинающих
ВходРегистрация
/
Программирование
/
Курс Golang
/

Nil

Golang: Nil

В языке Go значение nil используется для обозначения отсутствия данных. Оно является нулевым значением для типов, которые работают с указателями (указатели, срезы, карты, каналы, функции, интерфейсы). Рассмотрим примеры тех типов, которые мы уже изучили

nil для указателей

var ptr *int
fmt.Println(ptr == nil) // => true

Переменная указательного типа содержит nil, если не была явно инициализирована. Это означает, что она не указывает ни на какую область памяти.

Попытка разыменовать nil-указатель приводит к панике во время выполнения:

var ptr *int
fmt.Println(*ptr) // panic: invalid memory address or nil pointer dereference

Чтобы избежать ошибки, перед разыменованием указателя выполняется проверка:

if ptr != nil {
	fmt.Println(*ptr)
}

nil в полях структур

Если структура содержит указательное поле, это поле по умолчанию будет nil, если его не инициализировать.

type User struct {
	Name *string
}

func main() {
	var u User
	fmt.Println(u.Name == nil) // => true
}

Такую структуру можно проверить перед использованием указателя в поле:

if u.Name != nil {
	fmt.Println(*u.Name)
}

Вызов методов на nil

Если метод вызывается на значении, равном nil, и этот метод не обращается к полям внутри объекта, то ошибка не возникает:

type Logger struct{}

func (l *Logger) Info() {
	fmt.Println("Log info") // => Log info
}

func main() {
	var l *Logger
	l.Info() // работает, несмотря на то что l == nil
}

Такое поведение разрешено в Go по дизайну. Аргумент l — это просто указатель на Logger, и метод может быть вызван даже на nil, если внутри метода нет обращения к данным. Это позволяет, например, реализовать "пустые" или "заглушки" (no-op реализации), где метод ничего не делает, но при этом вызывается безопасно.

Но если метод попытается обратиться к данным внутри структуры, произойдёт паника:

type User struct {
	Name string
}

func (u *User) PrintName() {
	fmt.Println(u.Name)
}

func main() {
	var u *User
	u.PrintName() // panic: invalid memory address or nil pointer dereference
}

Вывод

nil — это значение по умолчанию для ссылочных типов, таких как указатели. Оно часто используется в Go и требует проверки перед обращением к данным.

Задание

Создайте структуру Config с полями:

  • Host — указатель на строку (*string),
  • Port — число (int).

Реализуйте функцию PrintConfig(cfg *Config), которая должна:

  1. Проверять, инициализировано ли поле Host.

    • Если Host == nil, выводить "Host is not set".
    • Если Host != nil, выводить значение строки Host.
  2. Всегда выводить значение поля Port.

Пример использования:

host := "localhost"
cfg := &Config{Host: &host, Port: 8080}
PrintConfig(cfg)
// Вывод:
// Host: localhost
// Port: 8080

cfg2 := &Config{Port: 3000}
PrintConfig(cfg2)
// Вывод:
// Host is not set
// Port: 3000
Коммерческий опыт и Трудоустройство

Команда проекта находится в телеграм-сообществе. Там можно задать любой вопрос и повлиять на проект

Если вы зашли в тупик, то самое время поговорить с нашим асситентом Тота во вкладке "ИИ-помощник":

Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.

Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи. В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.

Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в обратной связи нашего сообщества

Нашли ошибку? Есть что добавить? Пулреквесты приветствуются
/
Программирование
/
Курс Golang
/

Nil

Golang: Nil

В языке Go значение nil используется для обозначения отсутствия данных. Оно является нулевым значением для типов, которые работают с указателями (указатели, срезы, карты, каналы, функции, интерфейсы). Рассмотрим примеры тех типов, которые мы уже изучили

nil для указателей

var ptr *int
fmt.Println(ptr == nil) // => true

Переменная указательного типа содержит nil, если не была явно инициализирована. Это означает, что она не указывает ни на какую область памяти.

Попытка разыменовать nil-указатель приводит к панике во время выполнения:

var ptr *int
fmt.Println(*ptr) // panic: invalid memory address or nil pointer dereference

Чтобы избежать ошибки, перед разыменованием указателя выполняется проверка:

if ptr != nil {
	fmt.Println(*ptr)
}

nil в полях структур

Если структура содержит указательное поле, это поле по умолчанию будет nil, если его не инициализировать.

type User struct {
	Name *string
}

func main() {
	var u User
	fmt.Println(u.Name == nil) // => true
}

Такую структуру можно проверить перед использованием указателя в поле:

if u.Name != nil {
	fmt.Println(*u.Name)
}

Вызов методов на nil

Если метод вызывается на значении, равном nil, и этот метод не обращается к полям внутри объекта, то ошибка не возникает:

type Logger struct{}

func (l *Logger) Info() {
	fmt.Println("Log info") // => Log info
}

func main() {
	var l *Logger
	l.Info() // работает, несмотря на то что l == nil
}

Такое поведение разрешено в Go по дизайну. Аргумент l — это просто указатель на Logger, и метод может быть вызван даже на nil, если внутри метода нет обращения к данным. Это позволяет, например, реализовать "пустые" или "заглушки" (no-op реализации), где метод ничего не делает, но при этом вызывается безопасно.

Но если метод попытается обратиться к данным внутри структуры, произойдёт паника:

type User struct {
	Name string
}

func (u *User) PrintName() {
	fmt.Println(u.Name)
}

func main() {
	var u *User
	u.PrintName() // panic: invalid memory address or nil pointer dereference
}

Вывод

nil — это значение по умолчанию для ссылочных типов, таких как указатели. Оно часто используется в Go и требует проверки перед обращением к данным.

Задание

Создайте структуру Config с полями:

  • Host — указатель на строку (*string),
  • Port — число (int).

Реализуйте функцию PrintConfig(cfg *Config), которая должна:

  1. Проверять, инициализировано ли поле Host.

    • Если Host == nil, выводить "Host is not set".
    • Если Host != nil, выводить значение строки Host.
  2. Всегда выводить значение поля Port.

Пример использования:

host := "localhost"
cfg := &Config{Host: &host, Port: 8080}
PrintConfig(cfg)
// Вывод:
// Host: localhost
// Port: 8080

cfg2 := &Config{Port: 3000}
PrintConfig(cfg2)
// Вывод:
// Host is not set
// Port: 3000
Коммерческий опыт и Трудоустройство

Команда проекта находится в телеграм-сообществе. Там можно задать любой вопрос и повлиять на проект

Если вы зашли в тупик, то самое время поговорить с нашим асситентом Тота во вкладке "ИИ-помощник":

Тесты устроены таким образом, что они проверяют решение разными способами и на разных данных. Часто решение работает с одними входными данными, но не работает с другими. Чтобы разобраться с этим моментом, изучите вкладку «Тесты» и внимательно посмотрите на вывод ошибок, в котором есть подсказки.

Это нормально 🙆, в программировании одну задачу можно выполнить множеством способов. Если ваш код прошел проверку, то он соответствует условиям задачи. В редких случаях бывает, что решение подогнано под тесты, но это видно сразу.

Создавать обучающие материалы, понятные для всех без исключения, довольно сложно. Мы очень стараемся, но всегда есть что улучшать. Если вы встретили материал, который вам непонятен, опишите проблему в обратной связи нашего сообщества

Нашли ошибку? Есть что добавить? Пулреквесты приветствуются
← ПредыдущийСледующий →
← ПредыдущийСледующий →
← ПредыдущийСледующий →

Ваше упражнение проверяется по этим тестам

package solution

import (
	"bytes"
	"io"
	"os"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestPrintConfig(t *testing.T) {
	tests := []struct {
		name     string
		cfg      *Config
		expected string
	}{
		{
			name:     "With host",
			cfg:      &Config{Host: strPtr("localhost"), Port: 8080},
			expected: "Host: localhost\nPort: 8080\n",
		},
		{
			name:     "Without host",
			cfg:      &Config{Port: 3000},
			expected: "Host is not set\nPort: 3000\n",
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			// Создаём pipe для перехвата вывода
			r, w, err := os.Pipe()
			assert.NoError(t, err)

			stdout := os.Stdout
			os.Stdout = w

			// Выполняем тестируемую функцию
			PrintConfig(tt.cfg)

			// Закрываем writer и проверяем ошибку
			closeErr := w.Close()
			assert.NoError(t, closeErr)

			// Восстанавливаем Stdout
			os.Stdout = stdout

			// Читаем перехваченный вывод
			var buf bytes.Buffer
			_, err = io.Copy(&buf, r)
			assert.NoError(t, err)

			// Проверка результата
			assert.Equal(t, tt.expected, buf.String())
		})
	}
}

func strPtr(s string) *string {
	return &s
}
← ПредыдущийСледующий →

Решение учителя откроется через:

20:00

waiting_clock
← ПредыдущийСледующий →