Пишу, по большей части, про историю, свою жизнь и немного про программирование.

Язык программирования «Go»

В этом году я решил выучить язык программирования «Go». Язык разработан в стенах «Гугла» и это один из проектов, который у «Гугла», что называется, «не взлетел». В компании не очень-то занимаются его продвижением и поддержкой, если ничего не изменится, то язык, по всей видимости, будет обречён на забвение.

Самое интересное в языке — это то, как он запросто справляется с параллелизацией задач, но я пока не успел это попробовать, видел лишь на примерах. Выглядит очень круто. Интересен он ещё и тем, что компилируется (архитектуры AMD64, i386 и ARM), но при этом сам умеет управлять памятью. Ошибочно считается, что в языке нет «традиционных» указателей (с арифметикой), это не так, там они есть, просто спрятаны в модуль.

Компиляция, как утверждается, даёт хороший прирост скорости (всего лишь в 5-10 раз медленее Си и в сотни раз быстрее PHP). При этом моя небольшая программа (см. ниже) скомпилировалась в файл размером 5,8 мегабайт.

Язык сильно меняется со временем, можно заметить, что примеры кода, которым только год, уже не компилируются. Документация по некоторым модулям не просто скудная, её нет, взгляните, к примеру, на функцию Accept модуля syscall.

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

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

Оказалось, что на «Go» программа решается буквально в несколько строк, поэтому это было неинтересно. Я усложнил задачу — после запуска программа должна была сказать по какому IP-адресу можно будет зайти браузером.

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

Я использовал модуль syscall и вызвал системный вызов ioctl с параметром SIOCGIFCONF. Из-за того, что язык самовольно выравнивает структуру (struct) по границе 8 байт (надо бы проверить будет ли тот же эффект на архитектуре с 32 битами), я очень долго не мог понять почему мой вызов даёт ошибку.

Дальше всё просто — я получаю все адреса IPv4 (можно и IPv6, но мне пока не хочется возиться), если их несколько, то убираю из них те, что относятся к Private IP (10/8, 172.16/12, 192.168/16) и возвращаю один из тех, что остались. В противном случае, возвращаю тот единственный, что у меня в наличии.

Сама программа ниже:

// httphere 0.1. Написана Евгений Степанищевым, май 2011. //bolknote.ru
package main

import (
    "http"
    "flag"
    "strconv"
    "os"
    "syscall"
    "unsafe"
    "strings"
    "path"
    "path/filepath"
)

const IF_BUFFER int = 2048

type ifconf struct {
    _   int32
    len int32              "Размер буфера"
    ifr unsafe.Pointer  "Указатель на буфер"
}

func exit(message string, code int) {
    os.Stderr.WriteString("Error: " + message + "\n")
    os.Exit(code)
}

/* Проверка входного параметра (порт) на допустимые значения */
func checkargs(port int) {
    if port > 65535 || port < 1 {
        exit("invalid port range", 1)
    }

    if syscall.Geteuid() > 0 && port < 1024 {
        exit("if you want to use ports lower than 1024, you must run httphere as the root user", 3)
    }
}

/* преобразование IP-адреса в строку */
func joinip4(ip []byte) string {
    var ipstr []string

    for _, s := range ip {
        ipstr = append(ipstr, strconv.Itoa(int(s)))
    }

    return strings.Join(ipstr, ".")
}

/* получаем список IPv4-адресов на внешних интерфейсах */
func getallip4() (iplist []([]byte)) {
    fd, errno := syscall.Socket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP)
    if err := os.NewSyscallError("socket", int(errno)); err == nil {
        defer syscall.Close(fd)

        ifr := new([IF_BUFFER]byte)
        ifc := ifconf{len: int32(unsafe.Sizeof(*ifr)), ifr: unsafe.Pointer(ifr)}

        ifcwopadding := uintptr(unsafe.Pointer(uintptr(unsafe.Pointer(&ifc)) + 4))
        _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), uintptr(syscall.SIOCGIFCONF), ifcwopadding)

        if err := os.NewSyscallError("ioctl", int(errno)); err != nil {
            return
        }

        for i := int32(0); i < ifc.len; {
            i += 16 /* skip interface name */
            length, family := int32(ifr[i]), ifr[i+1]

            sockaddr := ifr[i+2:i+length]
            i += length

            if family == syscall.AF_INET {
                iplist = append(iplist, sockaddr[2:6]) 
            }
        }
    }

    return
}

/* на вход подаётся список IPv4-адресов, если их несколько, то функция выбрасывает локальные.
   на выходе — один IPv4 
*/
func filterip4(iplist []([]byte)) []byte {
    switch len(iplist) {
        case 0:
            return []byte{127, 0, 0, 1}

        case 1:
            return iplist[0]
    }

    var private []byte = nil

    for _, ip := range iplist {
        if ip[0] == 192 && ip[1] == 168 || ip[0] == 10 || ip[0] == 172 && ip[1] >= 16 && ip[1] <= 31 {
            private = ip
        } else if ip[0] != 127 {
            return ip
        }
    }

    if private != nil {
        return private
    }

    return iplist[0]
}

func main() {
    port := flag.Int("port", 8080, "http port address (1-65535)")
    flag.Parse()
    checkargs(*port)

    portstr := strconv.Itoa(*port)
    ip := joinip4(filterip4(getallip4()))

    os.Stdout.WriteString("Listen on " + portstr + ". Go to http://" + ip + ":" + portstr + "/\n")

    http.Handle("/", http.HandlerFunc(handler))
    if err := http.ListenAndServe(":" + portstr, nil); err != nil {
        exit(err.String(), 2)
    }
}

func handler(w http.ResponseWriter, r *http.Request) {
    cwd, _ := os.Getwd()
    path := path.Join(cwd, filepath.Clean(r.URL.Path))

    http.ServeFile(w, r, path)
}

У программы есть единственный параметр (он выводится, если запустить программу с ключом „—help“) — можно указать порт, который программа будет слушать (по умолчанию использутеся 8080).

Программа прерывается по Ctrl+C.

17 комментариев
Orcinus Orca (orcinus.ru) 2011

Чем-то он мне Паскаль напоминает. И не только символом присваивания. А как вообще впечатление от проекта, кроме как то что он скоро умрет?

Евгений Степанищев (bolknote.ru) 2011

Комментарий для orcinus.ru:

Чем-то он мне Паскаль напоминает. И не только символом присваивания.

А чем ещё, кроме символа? :) Тем более, что „:=“ — синтаксический сахар, на самом деле:

s := «Привет»
эквивалентно
var s = «Привет»

А как вообще впечатление от проекта, кроме как то что он скоро умрет?

Я немного описал впечатления вот тут: http://bolknote.ru/all/3198

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

В общем-то, мне пока скучновато его изучать, единственное от чего реально сносит крышу — это его многозадачность. Она там выглядит, как башня из слоновой кости посредине вселенской грязищи. Но как попробовать её в деле я пока не придумал. Обязательно надо попробовать, программировать на «Go» без многозадачности — деньги на ветер.

Orcinus Orca (orcinus.ru) 2011

Многозадачность? Нарисуй задачу по построению фракталов, там можно на подзадачи дробить до бесконечности. Добавь уровень моря, и все, что глубже рисуй синим, а над уровнем — зеленым.

Евгений Степанищев (bolknote.ru) 2011

Комментарий для orcinus.ru:

Да нет, я что-нибудь полезное для себя хочу :) Думаю не переписать ли скруглятор уголков на «Go», там есть пакетная обработка файлов, вполне можно распараллелить это.

rodem 2011

Насчёт выстрелил/умер не всё так однозначно.
Изначально Go не позиционировался как массовый язык для всех. Rob Pike на всех конференциях удивляется что им так сильно интересуются web программисты, хотя они не для этого его писали.
На любой конференции или другом выступлении его постоянно спрашивают «Какой проект продашкн уровня работает на Го?». Последнее что я слышал, это то что несколько рабочих групп внутри Google перешли с Java/Python на Go, но что это за внутренние разработки, он сказать не может.
Через неделю Googe IO. Там будет доклад по Go:
Andrew Gerrand, Rob Pike
Writing Web Apps in Go
May 10, 11:30AM — 12:30PM

В дополнение к документации есть ещё и пара книжек по Go:
http://www.miek.nl/
http://miek.nl/downloads/2011/go.pdf

Насчёт ниши языка не очень понятно, если честно. Явно не массовый. Как erlang.

Евгений Степанищев (bolknote.ru) 2011

Комментарий для rodem:

За ссылки на книжки спасибо большое! Я даже не подозревал, что они есть.

Rob Pike на всех конференциях удивляется что им так сильно интересуются web программисты, хотя они не для этого его писали.

Странно, на сайте «Гоу» есть «Writing Web Applications» ( http://golang.org/doc/codelab/wiki/ )

rodem 2011

Комментарий для Евгения Степанищева:

Вот сам сейчас эту книжку вместе с Effective Go почитываю.
Видимо они посмотрели сколько сразу желающих построить свой Djang/Rails аналог на Go и выктили пример с wiki. Там много разные web проектов было.
Даже для IDE появлялись плагины. Eclipse/IntelliJ.

http://go-lang.cat-v.org/
ещё вот тут много ссылок на разные проекты.

Бурного развития в Go нет, но большинству это и не важно. В прошлом году все яростно желали Go for App Engine. Пайк сказал, что в этом направлении они работают с командой App Engine, но результат будет не скоро. Это пожалуй единственное, что может дать массовый толчок развитию языка.

rodem 2011

Комментарий для Евгения Степанищева:

Тут как-то ещё на Хабре в обсуждении проскакивало, что в гугл не видят перспектив для Python, так как при прочих равных Java/C в разы быстрее и менее прожорливы в памяти. А так как лозунг «Скорость — наше всё!» у них на знамени уже давненько, возможно просто все направления параллельно развивают и GoLang как одно из возможных.

rodem 2011

Комментарий для Евгения Степанищева:

Всё забываю спросить, как оно без FARа на маке? )

Евгений Степанищев (bolknote.ru) 2011

Комментарий для rodem:

в гугл не видят перспектив для Python, так как при прочих равных Java/C в разы быстрее и менее прожорливы в памяти

С Java сравнивать не берусь, мало опыта в этом языке, а Пайтон с Си сравнивать совершенно некорректно. Да, Си быстрее, но разработка на Пайтоне на порядки выше, при стоимости железки меньше, чем зп программиста за месяц, смешно говорить о том, что Си побивает Пайтон по скорости. Да, есть задачи, где это критично, но это не 99% всех веб-приложений, там хватает скорости Пайтона, плюс навыки в шардинге и прочим размазывании задач по машинам.

Всё забываю спросить, как оно без FARа на маке? )

Пока выбираю себе IDE, в перерывах между ними пишу в VIM :)

Евгений Степанищев (bolknote.ru) 2011

Простой «Hello, world» занял на этом языке 300КБ, мне удалось выяснить, что наибольший вклад в 5,8МБ, которые занимает эта утилита, вносит модуль http. Если добавить его в мой «Hello, world», то объём сразу увеличивается до 5,7МБ.

Orcinus Orca (orcinus.ru) 2011

Комментарий для Евгения Степанищева:

А в скругляторе уголков реально задать цвет того фона, который будет округлять фотографию? Ну там, ини-файл предусмотреть или прямо в командной строке указать... Я смотрел, ты вроде бы просто белым цветом все заполняешь.

Евгений Степанищев (bolknote.ru) 2011

Комментарий для orcinus.ru:

А в скругляторе уголков реально задать цвет того фона, который будет округлять фотографию?

Параметр «background»

www.shcoder.by 2011

Женя, а вот зачем ты изучаешь новые языки? Можешь, кратко сформулировать причины? :)
Я тут ввязался в небольшой холи-вар на похожую тему, собираюсь с силами для поста на тему «зачем нужно учить новые языки», присоединяйся к дискуссии.
<a href=» http://www.shcoder.by/2011/05/blog-post.html%22%3E%D0%AF%D0%B7%D1%8B%D0%BA%D0%B8​, разработчики и менеджеры</a> — мое
<a href=» http://www.it4business.ru/lib/2499/%22%3E%D0%9D%D0%B0 языке менеджеров и программистов</a> — Славы Панкратова
<a href=» http://www.shcoder.by/2011/05/blog-post_05.html%22%3E%D0%9A%D1%81%D0%B5%D0%BD%D0%BE%D0%B3%D0%BB%D0%BE%D1%81%D1%81%D0%BE%D1%84%D0%BE%D0%B1%D0%B8%D1%8F или боязнь незнакомых языков </a> — снова мое

Евгений Степанищев (bolknote.ru) 2011

Комментарий для www.shcoder.by:

Ой, мама, сколько ссылок :)

Я сейчас собеседование буду проводить, потом посмотрю.

Можешь, кратко сформулировать причины? :)

Причин несколько:
1) тренировать мозг непривычным, если мозг не тренировать он «вянет»
2) класть в мозг непривычные концепции, рассматривая их «в поле», на практике. Это увеличивает мой опыт и расширяет кругозор
3) мне это нравится. Я испытываю удовольствие, когда обнаруживаю как-то язык, в котором есть красивый приём. Эстетическое удовольствие.
4) быть на переднем крае. Я нередко смотрю новые языки, которые появляются (не обязательно так подробно как «Go» или «Powershell»), потом, при выборе решения, я смогу сказать какой язык лучше выбрать в качестве инструмента
5) некоторые языки приходится учить, потому что в коллективе, куда я перехожу они приняты

Test 2014

Язык разработан в стенах «Гугла» и это один из проектов, который у «Гугла», что называется, «не взлетел»

Полнейшая чушь, язык взлетит, если им будет заниматься сообщество. Язык разработан, все довольны (Heroku и Github например его уже используют), язык не гугла, а Роба Пайка.

скомпилировалась в файл размером 5,8 мегабайт

Чушь, по дефолту там статическая линковка (что просто супер), поэтому бинарники такие громадные (но переносимые)

Евгений Степанищев (bolknote.ru) 2014

Комментарий для Test:

Что вас подвигло прокомментировать заметку, которая написана три года назад, да ещё и утверждать, что тогда язык «взлетел»? Интерес к нему был минимален, сообщество было крошечным, нехватало элементарных модулей (например, работы с графиков).

Чушь, по дефолту там статическая линковка (что просто супер), поэтому бинарники такие громадные (но переносимые)

В чём чушь? Файл скомпилировался в 5,8 мегабайт. Это факт, а не чушь.