
Когда человек начинает писать на непривычном языке программирования, он всегда обращает внимание на его особенности. Новичку бывает сложно понять причины такого дизайна языка. Своим студентам мы даем необходимый контекст, и постепенно они учатся программировать, учитывая и принимая то, что раньше выводило их из равновесия. Автор статьи разбирает особенности Go, которые смущают начинающих.
Сразу скажу, что эта статья: мое личное, полностью субъективное мнение. Список ниже — только небольшая выдержка без каких-либо критериев выбора. Для ясности расскажу о себе: у меня около 20 лет опыта работы, я работал с C, C++, Java, Scala, Python, R (если смотреть на R как на язык).
Я нахожу Go легким в изучении. Наверное, благодаря четко определенному замыслу, который устраняет особенности, подразумевающие сложный синтаксис. Так или иначе, я начинаю список.
1. Нежелательное импортирование и лишние переменные
Go заставляет придерживаться минимализма. Это означает, что бесполезное импортирование и лишние переменные вызовут ошибку компиляции. Например:
import ( "fmt" "os" //not used ) func main() { fmt.Println("Hola") }
Компилятор возвращает:
imported and not used: "os"
2. Итерация по коллекциям
Функция range, используемая при итерации по коллекции, возвращает два значения. Первое значение — это позиция элемента в коллекции. Второе значение — это значение самого элемента.
x := [4]string{"one","two","three","four"} for i, entry := range(x) { fmt.Printf("Element at position %d is %s\n", i, entry) }
Это очень удобно: на каждой итерации есть два наиболее распространенных значения, с которыми можно работать в своих циклах. Но не всегда нужно два значения. Наверное, вы напишете что-то вроде этого:
x := [4]string{"one","two","three","four"} for i, entry := range(x) { fmt.Printf("Element %s\n", entry) }
И такой код вызовет ошибку компиляции:
i declared but not used
Или даже хуже, вы пропустите i. Вот так:
x := [4]string{"one","two","three","four"} for entry := range(x) { fmt.Printf("Element %s\n", entry) }
Это может запутать. В переменной возвращается позиция элемента, но можно ожидать его значение.
Element %!s(int=0) Element %!s(int=1) Element %!s(int=2) Element %!s(int=3)
Как решить проблему? Нужно просто обозначить неиспользуемую переменную i вот так:
x := [4]string{"one","two","three","four"} for _, entry := range(x) { fmt.Printf("Element %s\n", entry) }
3. Видимость атрибутов
Атрибуты видимы, когда начинаются с заглавной буквы. Атрибут, который не начинается с заглавной буквы, не видим. Это просто. Но я постоянно забываю об этом и делаю глупые ошибки.
type Message struct { Text string // This is public text string // This is private }
4. Что с перегрузкой методов?
Никакой перегрузки методов нет. Если вы пришли из мира Java, скорее всего вы применяли перегрузку методов. В Golang перегрузки методов нет.
5. А наследование?
Наследования тоже нет. Эту особенность можно обойти, как описано здесь. Но я не думаю, что это действительно наследование.
6. Интерфейсы в Go
В отличие от перегрузки методов и наследования, интерфейсы в Go есть. Вы можете определить их как набор из сигнатур методов. Но они странные в сравнении с интерфейсами в других языках. Почему? Потому что вы не указываете программно, что структура реализует интерфейс. Структура автоматически удовлетворяет интерфейсу, когда реализует перечисленные в интерфейсе методы. Это проще понять на примере:
package main import ( "fmt" ) type Speaker interface { SayYourName() string SayHello(b Speaker) string } type HappySpeaker struct {} func(hs HappySpeaker) SayYourName() string { return "Happy" } func(hs HappySpeaker) SayHello(b Speaker) string { return fmt.Sprintf("Hello %s!",b.SayYourName()) } type AngrySpeaker struct {} func(as AngrySpeaker) SayYourName() string { return "Angry" } func(as AngrySpeaker) SayHello(b Speaker) string { return fmt.Sprintf("I'm not going to say hello to %s!",b.SayYourName()) } func main() { // We have two different structs happy := HappySpeaker{} angry := AngrySpeaker{} // they can say their names fmt.Println(happy.SayYourName()) fmt.Println(angry.SayYourName()) // But they are also speakers fmt.Println(happy.SayHello(angry)) fmt.Println(angry.SayHello(happy)) // This is also valid var mrSpeaker Speaker = happy fmt.Println(mrSpeaker.SayHello(angry)) }
Вполне понятно, что такое поведение языка влияет на код. Интерфейсы в Go — тема для подробной дискуссии. Вы найдете множество обсуждений достоинств и недостатков этой особенности языка.
7. Конструкторы
В Go нет конструкторов, подобных тем, которые вы найдете в объектно-ориентированном языке. Определение структуры на Go очень похоже на определение структуры в языке C. Но есть одна потенциальная проблема: вы можете пропустить инициализацию атрибутов. В коде ниже у halfMessage1 и halfMessage2 пустые атрибуты.
import ( "fmt" ) type Message struct { MsgA string MsgB string } func(m Message) SayIt() { fmt.Printf("[%s] - [%s]\n",m.MsgA, m.MsgB) } func main() { fullMessage1 := Message{"hello","bye"} fullMessage2 := Message{MsgA: "hello", MsgB: "bye"} halfMessage1 := Message{"hello",""} halfMessage2 := Message{MsgA: "hello"} emptyMessage := Message{} fullMessage1.SayIt() fullMessage2.SayIt() halfMessage1.SayIt() halfMessage2.SayIt() emptyMessage.SayIt() }
Код выше выведет:
[hello] - [bye] [hello] - [bye] [hello] - [] [hello] - [] [] - []
Потенциально это проблема потому, что у вас могут быть методы, которые ожидают каких-то значений. Смягчить ситуацию можно определением статического конструктора.
package main import ( "fmt" ) type Message struct { MsgA string MsgB string } func(m Message) SayIt() { fmt.Printf("[%s] - [%s]\n",m.MsgA, m.MsgB) } func NewMessage(msgA string, msgB string) *Message{ if len(msgA) * len(msgB) == 0 { return nil } return &Message{MsgA: msgA, MsgB: msgB} } func main() { // A correct message msg1 := NewMessage("hello","bye") if msg1 != nil { msg1.SayIt() } else { fmt.Println("There was an error") } // An incorrect message msg2 := NewMessage("","") if msg2 != nil { msg2.SayIt() } else { fmt.Println("There was an error") } }
Заключение
Это была небольшая выборка особенностей, которые следует учитывать, когда вы программируете на Go. А что в нем показалось вам самым странным при программировании на Go?
Получить востребованную профессию с нуля или Level Up по навыкам и зарплате, можно пройдя онлайн-курсы SkillFactory:
- Курс «Backend-разработчик на Go»
- Курс «Python для веб-разработки»
- Профессия Веб-разработчик
- Курс по JavaScript
Eще курсы
- Обучение профессии Data Science с нуля
- Онлайн-буткемп по Data Science
- Онлайн-буткемп по Data Analytics
- Профессия аналитика с любым стартовым уровнем
- Курс по Machine Learning
- Курс «Математика и Machine Learning для Data Science»
- Продвинутый курс «Machine Learning Pro + Deep Learning»
- Курс по аналитике данных
- Курс по DevOps
- Профессия iOS-разработчик с нуля
- Профессия Android-разработчик с нуля
- Профессия Java-разработчик с нуля
- Профессия UX-дизайнер с нуля
- Профессия Web-дизайнер
ссылка на оригинал статьи https://habr.com/ru/company/skillfactory/blog/522222/

Добавить комментарий