Go

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск
Go
Golang.png
Класс языка:

многопоточный, императивный, структурированный

Тип исполнения:

компилируемый

Появился в:

2009

Автор(ы):

Роберт Гризмер, Роб Пайк и Кен Томпсон

Выпуск:

1.3.1[1] (13 августа 2014)

Система типов:

строгая, статическая, с выводом типов

Основные реализации:

gc (8g, 6g, 5g), gccgo

Испытал влияние:

Си, Python, Оберон-2, Активный Оберон, Limbo

Лицензия

BSD

Сайт:

golang.org

Go (часто также Golang) — компилируемый, многопоточный язык программирования, разработанный компанией Google[2]. Первоначальная разработка Go началась в сентябре 2007 года, а его непосредственным проектированием занимались Роберт Гризмер, Роб Пайк и Кен Томпсон[3] занимавшиеся до этого проектом разработки операционной системы Inferno. Официально язык был представлен в ноябре 2009 года. На данный момент его поддержка осуществляется для операционных систем: FreeBSD, OpenBSD, Linux, Mac OS X, Windows[4], начиная с версии 1.3 в язык Go включена экспериментальная поддержка DragonFly BSD, Plan 9 и Solaris.

Название[править | править вики-текст]

Следует отметить, что название языка, выбранное компанией Google, практически совпадает с названием языка программирования Go!, созданного Ф. Джи. МакКейбом и К. Л. Кларком в 2003 году.[5] Обсуждение названия ведётся на странице, посвящённой Go[5].

Описание[править | править вики-текст]

Синтаксис языка Go схож с синтаксисом языка Си, с отдельными элементами, заимствованными из Оберона и скриптовых языков. В коде на Go очень мало точек с запятой — как точку с запятой Go трактует конец непустой строки (при определённых условиях). В результате этого в ряде случаев нельзя использовать перенос строки, например, в таком:

func g()
{                  // НЕВЕРНО
}

или таком

if x {
}
else {             // НЕВЕРНО
}

Синтаксис объявления типа, в основном, решён в духе Паскаля.

//Go                           C++
  var v1 int                // int v1;
  var v2 string             // const std::string v2;  (примерно)
  var v3 [10]int            // int v3[10];
  var v4 []int              // int* v4;  (примерно)
  var v5 struct { f int }   // struct { int f; } v5;
  var v6 *int               // int* v6;  (но нет арифметики для указателей)
  var v7 map[string]int     // unordered_map* v7;  (примерно)
  var v8 func(a int) int    // int (*v8)(int a);

При объявлении переменные инициализируются на нулевое значение для данного типа (0 для int, пустая строка для string, nil для указателей).

Объявления можно группировать:

var (
	i int
	m float
)

Язык Go поддерживает также автоматический вывод типов. Переменная может быть инициализирована при объявлении, её тип при этом можно не указывать — типом переменной становится тип присваиваемого ей выражения.

var v = *p

Внутри функции, короткий синтаксис присвоения переменным значения с автоматическим выводом типов напоминает обычное присваивание в Паскале.

v1 := v2 // аналог var v1 = v2

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

i, j = j, i    // Поменять местами значения i и j.

Аргументы функций и методов объявляются таким образом:

func f(i, j, k int, s, t string) string { }

Функции могут возвращать несколько значений; типы таких значений заключаются в скобки:

func f(a, b int) (int, string) {
	return a+b, "сложение"
}

Результаты функций также могут быть именованы:

func incTwo(a, b int) (c, d int) {
	c = a+1
	d = b+1
	return
}

Несколько значений, возвращаемых функциями, присваиваются переменным их перечислением через запятую:

first, second := incTwo(1, 2) // first = 2, second = 3

Прочие синтаксические различия заключаются в отсутствии круглых скобок для условных конструкций for и if.

Язык поддерживает сборку мусора (garbage collection), ассоциативные массивы и строки.

В Go отсутствуют такие возможности как:

Однако разработчики языка обдумывают возможность расширения языка средствами обобщённого программирования, в то время как в Часто задаваемых вопросах[3] по языку приводятся аргументы против использования утверждений, а наследование без указания типа, наоборот, отстаивается.

Многопоточность[править | править вики-текст]

Модель многопоточности Go была создана на основе CSP (англ.) Тони Хоара по типу предыдущих распараллеливаемых языков программирования Occam и Limbo,[3], но также присутствуют такие особенности Пи-исчисления, как канальная передача.

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

func server(i int) {
	for {
		print(i)
		time.Sleep(10)
	}
}
go server(1)
go server(2)


В выражении go можно использовать замыкания.

var g int
go func(i int) {
	s := 0
	for j := 0; j < i; j++ { s += j }
	g = s
}(1000)

Для связи между go-процедурами используются каналы (встроенный тип chan), через которые можно передавать любые значения. Для передачи значения в канал используется <- в качестве бинарного оператора, для получения сообщения из канала — <- в качестве унарного оператора.

Go и объектно-ориентированное программирование[править | править вики-текст]

Ключевое слово class в Go отсутствует, для любого именованного типа (включая структуры и базовые типы вроде int) можно определить методы работы с ним.

type newInt int

Определение метода отличается от обычного определения функции тем, что указывается получатель англ. receiver (похож на указатель this в методе класса C++).

type myType struct { i int }
func (p *myType) get() int { return p.i }
func (p *myType) set(i int) { p.i = i }

Там где в классических объектно-ориентированных языках используются классы, в Go задействованы интерфейсы (похожи на абстрактные классы C++). В Go каждый тип, предоставляющий методы, обозначенные в интерфейсе, может трактоваться как реализация интерфейса, явного объявления не требуется.

type myInterface interface {
	get() int
	set(i int)
}

Объявленный выше тип myType реализует интерфейс myInterface, хотя это нигде не указано явно.

Такой подход к наследованию соответствует некоторым практическим тенденциям современного программирования. Так в знаменитой книге «банды четырёх» (Эрих Гамма и др.) о паттернах проектирования, в частности, написано:

« Зависимость от реализации может повлечь за собой проблемы при попытке повторного использования подкласса. Если хотя бы один аспект унаследованной реализации непригоден для новой предметной области, то приходится переписывать родительский класс или заменять его чем-то более подходящим. Такая зависимость ограничивает гибкость и возможности повторного использования. С проблемой можно справиться, если наследовать только абстрактным классам, поскольку в них обычно совсем нет реализации или она минимальна. »

Динамическая поддержка объектно-ориентированного программирования для Go осуществлена с помощью проекта GOOP[6].

Реализации[править | править вики-текст]

На данный момент существуют два основных компилятора Go:

  • 6g (и сопутствующие ему инструменты, вместе известные под названием gc) написан на Си с применением yacc/Bison для парсера
  • Gccgo — ещё один компилятор Go с клиентской частью, написанной на C++, и рекурсивным парсером, совмещённым со стандартным бэк-эндом GCC[7]. Поддержка Go доступна в GCC начиная с версии 4.6[8].

А так же перспективные разработки:

  • llgo — прослойка для компиляции в llvm написанная на самом go (находится в разработке)[9].
  • SSA interpreter — интерпретатор позволяющий запускать программы на go.

Примеры[править | править вики-текст]

Ниже представлен пример программы «Hello, World!» на языке Go.

package main
 
import "fmt"
 
func main() {
	fmt.Println("Hello, World!")
}

Пример реализации команды Unix echo:

package main
 
import (
	"os"
	"flag" // парсер параметров командной строки
)
 
var omitNewLine = flag.Bool("n", false, "не печатать знак новой строки")
 
const (
	Space = " "
	NewLine = "\n"
)
 
func main() {
	flag.Parse() // Сканирование списка аргументов и установка флагов
	var s string
	for i := 0; i < flag.NArg(); i++ {
		if i > 0 {
			s += Space
		}
		s += flag.Arg(i)
	}
	if !*omitNewLine {
		s += NewLine
	}
	os.Stdout.WriteString(s)
}

Примечания[править | править вики-текст]

Ссылки[править | править вики-текст]