Так ли токсичен синтаксис Rust?

fn main() {   println!("Hello, Rust!");   println!("... Goodbye!"); }

Синтаксис — это первое, на что обращают внимание разработчики, впервые столкнувшись с кодом на Rust. И сложно найти что-то другое, что вызывало бы больше негодования у новичков, и при этом такое, к чему совершенно спокойно относятся уже "понюхавшие пороха" Rust-программисты. Посмотрите, например, сколько эмоций вызывает синтаксис Rust у комментаторов одной из типичных новостей про Rust на OpenNET:

Язык не для меня, нахожу его немного убогим в плане синтаксиса, но мне нравится много идей тем, взять хотя бы управление памятью.

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

Самое интересное в этой истории то, что примеры на сайте раста типа "привет мир", почему то намного более читабельные чем то, что реально используется и лежит на гитхабе, а там адовый синтаксис с кучей спец символов и какая то ещё жуткая муть.

Сначала хотел указать вам на ошибку, но потом понял, что "синтоксис" в данном контексте, нужно понимать как "токсичный синтаксис". Вот это именно то, чего мне не хватало что бы охарактеризовать раст.
Теперь я точно знаю: Раст — это ЯП с ТОКСИЧНЫМ СИНТАКСИСОМ!

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

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

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

Как ни печально, но встречают язык по голому синтаксису. И синтаксис Rust тут не предлагает чего-то революционного. По сути это обычный C-подобный синтаксис, который имеют и многие другие языки, но с различными современными улучшениями. И они, как ни странно, делают его лучше других, в ряде случаев выразительнее.

Проблема в том, что сразу этого не видно. Чтобы оценить выразительность, нужно уже владеть теми концепциями, языковую запись которых требуется оценить. Более того, нужно также иметь представление о проблемах выражения подобных концепций в других языках и иметь в виду возможное наличие этих проблем при оценке. Поэтому концептуальная сложность самого языка может выглядеть на первый взгляд как переусложнение синтаксиса. На самом деле синтаксис Rust не так плох, как о нем говорят. Он имеет множество замечательных находок, которыми удобно пользоваться, и вопреки расхожему мнению, читаются программы на Rust достаточно хорошо.

Так давайте рассмотрим подробнее, чем именно синтаксис Rust не угодил своим критикам и почему никто из них не может предложить ничего лучше, когда их просят это сделать.

Фигурные скобки

Многие люди, которые критикуют языки программирования за использование фигурных скобок и других спецсимволов, часто хотят, чтобы программа выглядела подобно тексту на естественном языке. Но такой формат совершенно неудобен для записи программного кода, и вот почему: текст на естественном языке — это просто запись последовательного потока речи, тогда как текст программы — это чаще выражение структурных отношений между элементами кода. Очевидно, что графическими обозначениями зачастую можно добиться большей выразительности при записи структур, будь то спецсимволы или отступы.

Выделение блоков кода одними лишь отступами имеет свои преимущества, но чаще всего оно требует серьезной дисциплины от программиста. Особенно при перемещении фрагментов кода из одного места в другое, из одного уровня вложенности — в другой. Читаемость кода улучшается, но возрастает напряжение внимания при работе с кодом, возникает почва для появления досадных ошибок.

Интересно, что одна из главных проблем, которую должно было решить обозначение блоков отступами — это проблема несоответствия между отступами и реальной синтаксической группировкой инструкций. При этом человек прочитывает один порядок, ориентируясь на отступы, а парсер языка — другой порядок, ориентируясь на правило, по которому блок выделяется скобками:

if (test(x))     foo(x);     bar(x);

Должен ли bar вызываться только тогда, когда test завершился успешно? По правилам языка — нет, так как отсутствуют фигурные скобки, обозначающие блок. Но такое форматирование кода затрудняет для человека понимание этого. Все потому, что имеется лишняя избыточность в обозначении блоков: скобками — для парсера, отступами — для человека, и они могут не совпадать. Поэтому в Python решили отказаться от этой избыточности:

if test(x):     foo(x)     bar(x)

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

Появление подобных ошибок наталкивает на мысль, что избыточность в обозначении блоков — не так уж и плоха, она позволяет ввести дополнительные способы контроля. Более того, наличие скобок делает возможным автоматическое расставление отступов: зная, что код был отформатирован автоматически (например, по сохранению изменений в файл), программист может визуально убедиться, что никакая инструкция не съехала, все расставлено на своих позициях как надо.

Даже без автоформатирования, Rust решает проблему случайных отступов радикальным образом:

if test(x) {     foo(x); } bar(x);

В конструкциях if, else, for, while и так далее, блок требуется обязательно! Сокращенная, бесскобочная запись тела у данных операторов в языке просто отсутствует. С одной стороны, это "загрязняет" фигурными скобками простые условия, но отчасти это компенсируется тем, что теперь круглые скобки вокруг условного выражения становятся избыточными и их можно убрать.

Вообще, в Rust блоки очень важны, они являются самостоятельными программными объектами с особыми свойствами.

Во-первых, блоки обозначают область видимости переменных. По достижении конца блока все переменные, введенные внутри блока, уничтожаются и происходит освобождение занятых ими ресурсов:

... {     let mut data = data.lock().expect("Mutex should be lockable");     data.read();     data.write(); } ...

Здесь data вне блока — это мьютекс, содержащий некоторые данные. А data внутри блока — это специальная ссылка на защищенные мьютексом данные, которая формируется при блокировке мьютекса и после удаления которой исходный мьютекс будет разблокирован. То есть блок в данном случае ограничивает область блокировки мьютекса, и встречаться подобные блоки могут в любом месте, где допустимо размещение инструкций.

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

let value = {     let a = 1;     let b = 2;     a + b };

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

let value = if x < 42 { -1 } else { 1 };

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

fn add_two(a: isize) -> isize {     a + 2 }

вместо

fn add_two(a: isize) -> isize {     return a + 2; }

Для специальных блоков, таких как unsafe и async, появляется возможно оборачивать ими только часть выражения, что в ряде случаев удобно и делает код нагляднее:

let a = 5 + unsafe { an_unsafe_fn() };

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

Точка с запятой

Отдельного рассмотрения заслуживает то, как работает точка с запятой в Rust. Многим не нравятся языки, которые требуют окончания инструкций точкой с запятой: зачем, если и так перевод строки может означать завершение инструкции? Отказ от точки с запятой оправдан, если в языке любая строка по умолчанию является инструкцией, как в Python. Если же язык использует концепцию "все есть выражение", которая расширяет его выразительные возможности, то без специального терминатора, по которому можно отличить строку-инструкцию от строки-выражения, удобным в использовании может быть только язык с динамической типизацией, такой как Ruby. Да и в таком языке в ряде случаев подход "все есть выражение" приводит к неудобствам:

class Foo   def set_x val     @val = val     nil   end end

Здесь возникает необходимость вставлять nil в конце блока, чтобы set_x возвращал пустое значение, а не результат выражения @val = val, то есть не значение val. Если это еще терпимо, то ситуация становится совершенно неприемлемой в случае статических языков и использования ветвлений:

if x < 42 {     foo(x) } else {     bar(x) }

Здесь foo возвращает число, но мы его не используем, а bar возвращает "пусто". Какого типа должно быть значение всего условного выражения?

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

Rust решает эту проблему элегантным способом, который позволяет оставаться ему статически типизированным языком и при этом использовать преимущества подхода "все есть выражение" без особых хлопот. Способ заключается в том, что точка с запятой используется не как простой разделитель инструкций, а как оператор, превращающий выражение в инструкцию. При этом инструкция тоже интерпретируется как выражение, которое всегда возвращает пустое значение:

if x < 42 {     foo(x); } else {     bar(x) }

Здесь foo возвращает число, но мы его не используем, и нам не нужно, чтобы оно возвращалось из данной ветки условия, поэтому мы завершаем выражение точкой с запятой. При этом bar возвращает "пусто", а значит мы не обязаны ставить точку с запятой после его вызова (но можем и поставить, для симметрии), ибо типы возврата обеих веток уже совпали.

Интересно, что в совокупности с правилами владения и семантикой перемещения по умолчанию получается, что если объект был перемещен в выражение, которое закончилось точкой с запятой и объект никуда больше перемещен не был, то он удаляется:

let message = "Hello!".to_string(); message;  println!("{}", message); // error[E0382]: borrow of moved value: `message`

Так что инструкция a; становится аналогом drop(a). Это нужно помнить, чтобы не удивляться, когда компилятор откажется компилировать инструкцию, в которой производится попытка сохранить ссылку на перемещенное внутрь нее значение.

В итоге подобное концептуальное расширение функций точки с запятой весьма удобно в использовании. В подавляющем большинстве случаев оно ни к каким проблемам не приводит, благодаря контролю соответствия типов и времен жизни со стороны компилятора. Что же касается "загрязнения" кода точками с запятой, то благодаря тому, что теперь она не ставится в ряде распространенных случаев, доля непустых строк в программах на Rust, содержащих точку с запятой, составляет около 20%. Не так уж и много.

Постфиксная запись типа

Иногда простота синтаксиса является кажущейся. Например, частенько задается вопрос: зачем в Rust используется постфиксная запись типа, ведь это усложняет запись, еще и приходится отделять имена от типа с помощью двоеточия, тогда как префиксная запись, принятая в C/C++, этого не требует и выглядит чище. На самом деле префиксная запись чище только в ряде простых случаев использования, но в более сложных случаях она изрядно запутывает и парсер, и человека.

Вообще, какой следует выбрать объективный критерий простоты синтаксиса? Мне видится, что немаловажно для оценки простоты синтаксиса является простота его парсера. Простой парсер может стать важным преимуществом языка программирования, так как он ускоряет время компиляции, разбор исходных файлов в IDE и утилитах анализа и автоматического преобразования кода (статические анализаторы, автоформатеры, авторефакторы и пр.). Кроме того, простота разбора синтаксиса также важна и для человека: во-первых, человеку тоже приходится "парсить" текст программы при ее чтении, а во-вторых, однозначность синтаксиса упрощает поиск нужных строк по кодовой базе, например, среди множества проектов на GitHub.

Рассмотрим пример объявления переменной определенного типа.

В Rust:

let i: u64;

В C++:

uint64_t i;

Первый вариант — это "паскалевский" способ декларации типа. Хотя он дополнительно "загрязнен" символом двоеточия, однако в общем случае он проще, как для разбора парсером, так и для восприятия человеком.

Прочитав i в i: u64, парсер уже знает, что i — это имя переменной, прочитав : он знает, что дальше идет имя типа, прочитав u64 он знает, что это имя типа, даже не сверяясь со списком имеющихся типов (то есть не заглядывая в семантику). Такой подход избавляет от необходимости декларировать тип до того, как будет объявлена переменная этого типа. В C/C++ из-за этого приходится отдельно делать объявления, отдельно определения, и иногда изменение порядка деклараций может изменить семантику.

Для человеческого восприятия преимущества "паскалеской" записи раскрываются в сложных случаях:

int (*fns[16])(int * const * p);

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

Синтаксис же, принятый в Rust, отделяет обозначение типа от имени переменной:

let fns: [fn(*const *mut isize) -> isize; 16];

Обозначение типа при этом всегда одинаково, вне зависимости от того, указано ли оно в аннотации типа в инструкции let, в вызове size_of или в объявлении функции как тип возвращаемого значения.

Ну и стоит сказать, что "паскалевская" декларация упрощается в случае автовыведения типа:

let a = foo();

Тип просто не указывается. Отсутствует также и двоеточие, что улучшает читаемость кода в большинстве случаев использования переменных.

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

Зачем вообще нужен let?

Иногда let ругают за его избыточность и ненужность. Но обычно это делают те, кто не идет дальше элементарных примеров объявления переменных в Rust. Дело в том, что инструкция let занимается не объявлением переменных, а сопоставлением выражения с образцом (паттерном):

let PATTERN: EXPR_TYPE = EXPR;  let PATTERN = EXPR;

В аннотации типа, если она требуется, указывается тип значения выражения справа, а не тип отдельных переменных, которые могут вводиться внутри паттерна (а могут и не вводиться). При этом let также используется в условиях if и while, если вместо истинности логического выражения там необходимо проверить соответствие выражения образцу:

if let PATTERN = EXPR {     ... }  while let PATTERN = EXPR {     ... }

Сам синтаксис образца PATTERN общий для всех мест, где он может использоваться: помимо указанных let, if let и while let, это также оператор match, for и аргументы функций и замыканий (там уже нет надобности в отдельном слове let, так как ничего другого, кроме образца, использовать в этих местах нельзя).

struct Foo {     a: i32, }  let Foo { a: mut b } = Foo { a: 25 };

В примере выше производится деструктуризация значения типа Foo и вводится новая переменная с именем b, которая получает свое значение из поля a и в дальнейшем может измениться.

В простых случаях паттерн может состоять только из одного имени новой переменной, но это только частный случай. К тому же Rust не требует, чтобы значения с новыми именами были связаны именно в той же инструкции, где эти имена были объявлены:

let a;  a = 25;

Наконец, Rust поддерживает затенение переменных:

let a = 25; // `a` имеет числовой тип  let a = "hello"; // теперь `a` имеет строковый тип

Во всех этих случаях нужно явно отличать let a или let a = 25 от просто a или a = 25, и ключевое слово let прекрасно с этим справляется.

Сокращения в ключевых словах

Уж наверное только совсем ленивые критики синтаксиса Rust не "проехались" по сокращениям в ключевых словах. Зачастую дело выставляют таким образом, что Rust форсирует сокращения и сплошняком только из них и состоит. Но на самом деле из 40 ключевых слов языка сокращениями являются только 7:

fn, mut, mod, pub, impl, dyn, ref

При этом dyn и ref используются крайне редко. Дополнительно есть еще 5 коротких ключевых слов (в две и три буквы), которые не являются сокращениями:

if, as, let, for, use

Все остальные слова — длиннее. Некоторые из них сделаны длинными намеренно, например continue и return: они используются редко и должны быть хорошо заметны в коде, хотя первоначальные версии языка вместо них использовали слова cont и ret.

Тем не менее, больше всего претензий возникает к ключевому слову fn. Предлагают обычно либо его убрать совсем, либо заменить на function.

На самом деле fn — весьма неплохой выбор для достижения компромисса между двумя крайностями. Наличие длинного слова function особо не помогает при чтении кода, обычно определение функции и так заметно по остальной ее сигнатуре и обязательному блоку с ее телом. Оно только оттягивает на себя внимание от имени и аргументов функции. К тому же оно занимает слишком много места, что плохо сказывается не только на скорости набора, но в ряде случаев и на чтении кода, например, в мессенджерах.

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

let a: fn(i32, String); // `a` - указатель на функцию с двумя аргументами let b: (i32, String);   // `b` - кортеж из двух элементов

Без специального ключевого слова, предваряющего имя объявляемой функции, становится сложно искать строки объявления по кодовой базе, так как они синтаксически становятся похожи на вызовы.

Хорошо, но почему бы не выбрать сокращение подлиннее, такое как fun или func? Это довольно спорный вопрос даже в Rust-сообществе, но fn выглядит более нейтральным, так как не является другим самостоятельным словом и не имеет никаких посторонних значений по звучанию, так что оно хорошо ассоциируется с function и является минимально возможным его сокращением.

Говоря о коротких ключевых словах и спецсимволах, стоит заметить, что код с ними становится плохо читаемым в том случае, если программист также выбирает короткие имена для объектов программы. В таком случае глаз "спотыкается", становится сложно отличить пользовательские имена от элементов языка. В случае же использования длинных имен, дискомфорта не возникает, наоборот, глазу становится проще отделять имена от синтаксических конструкций, что улучшает восприятие кода. Поэтому такие слова как fn, mut, mod, pub, impl, dyn и ref, после которых идут пользовательские имена, не затрудняют, а улучшают чтение программы, если при этом программист выбирает длинные осмысленные имена для своих объектов, а не увлекается сокращениями.

Стрелка в сигнатуре функций

Другая частая претензия к объявлению функций в Rust, это использование символа "стрелки" для разделения блока параметров от типа возвращаемого функцией результата:

fn foo(x: i32) -> bool {   ... } 

Кажется, что последовательнее тут тоже использовать двоеточие:

fn foo(x: i32): bool {   ... }

Однако в таком случае создается впечатление, что тип bool относится ко всему выражению слева, то есть ко всей функции, тогда как на самом деле он является только частью функционального типа, указывающей тип возвращаемого значения. Проблему становится заметно еще отчетливее в случае объявления переменной функционального типа:

let foo: fn(x: i32) -> bool;

Против

let foo: fn(x: i32): bool;

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

В случае если функция не должна возвращать никакого значения, в Rust считается, что она возвращает значение "пусто" (), и тип возвращаемого значения можно не указывать. То есть

fn foo() -> () {     ... }

равнозначно

fn foo() {     ... }

Без подобного сахара для такого тривиального случая, от обилия спецсимволов действительно начало бы пестрить в глазах.

Замыкания

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

foo().map(|x| x + 2)

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

Текущий синтаксис прошел определенную историю развития, в процессе которого он избавился от всего лишнего и приобрел эту окончательную, легковесную и лаконичную форму. Использование вертикальных черт улучшает читаемость кода: замыкание хорошо выделяется в группе аргументов функций, где оно используется чаще всего, при этом его сложно спутать с кортежем или массивом.

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

foo().map(|_| ())

Конструкция |_| () выглядит некрасиво. Она означает, что мы игнорируем входной аргумент замыкания и возвращаем из замыкания пустое значение. Подобный map полезен, когда нужно преобразовать одно значение типа Result в другое с заменой положительного значения возврата на "пусто". Однако добиться этого можно и более наглядным образом, просто передав функцию drop вместо замыкания:

foo().map(drop)

Двоеточия в пути

В Rust используется двойное двоеточие :: в качестве квалификатора пространств имен, который разделяет сегменты в логических путях к элементам (items) языка, при том, что для доступа к полям и методам структур используется точка .. Это часто раздражает людей, привыкших к языкам, где для доступа к полям и методам используется тот же синтаксис, что и для доступа к элементам внутри пространства имен. Им кажется, что синтаксически разделять эти два обращения совершенно избыточно, и Rust использует подобное разделение либо в силу скудоумия своих создателей, либо из-за желания подражать C++.

Однако на самом деле, предоставляемые Rust возможности требуют явного синтаксического разделения статических обращений xx::yy::zz и динамических xx.yy.zz, где результат будет зависеть от значения объектов xx, yy и zz во время выполнения. Возможны ситуации, когда программист должен использовать канонический путь для обращения к методу, когда ему нужно явно указать тип или типаж, на котором будет вызван данный метод:

let foo = Foo; foo.bar();      // вызов метода `bar` из `Foo`  Bar::bar(&foo); // вызов метода `bar` из реализации типажа `Bar` для `Foo`

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

Foo::bar(&foo); // вызов метода `bar` из `Foo` у объекта `foo` Foo.bazz(&foo); // вызов метода `bazz` у созданного на месте объекта `Foo`

Программа, написанная на Rust, не имеет информации о типах во время выполнения, так как при компиляции происходит "стирание типов". Поэтому возникает необходимость различать динамические и статические обращения: механизм их работы сильно отличается, как и последствия, к которым они приводят, так что программист должен видеть это в коде явно. Однако в качестве утешения появляется возможность использовать одни и те же идентификаторы для имен переменных и модулей, что очень удобно.

Дженерики

Хотя код с использованием угловых скобок для обозначения параметров обобщенных типов <T> выглядит более "шумно", чем код с использованием квадратных скобок [T], тем не менее в Rust используется вариант с угловыми скобками, потому что он однозначно отделяет тип от контекста его использования, тогда как конструкция [T] — сама является обозначением типа (среза).

fn parse<F: FromStr>(&self) -> Result<F, F::Err> {     // ... }

Обобщенный метод parse на вход принимает тип F, реализующий типаж FromStr, и ссылку на экземпляр объекта &self, для которого реализуется данный метод. Возвращает же он обобщенный тип Result, в который передаются в качестве параметров типа F и ассоциированный с ним тип F::Err.

Угловые скобки используются для работы с обобщенными типами как в контексте объявления обобщенных элементов, так и в контексте выражения при подстановке уже конкретных типов. И с этим связана, пожалуй, самая уродливая конструкция языка — жуткий мутант, внушающий первобытный страх любому, кто случайно заглядывает в глубины "синтОксиса" кода на Rust. Имя этому монстру — Турбофиш:

"2021".parse::<usize>()

Вот этот страшный зверь ::<> и есть Турбофиш. К сожалению оказалось, что от него совсем не просто избавиться: конструкция A<B>() выглядела бы логичнее и лаконичнее, но в контексте выражения парсер не может отличить ее начало от операции сравнения A < B. Отсюда и возникает необходимость дополнить угловую скобку разделителем сегментов в пути ::. Турбофиш очень знаменит в сообществе разработчиков на Rust, и если он когда-нибудь все-таки будет устранен (что вряд ли), то этот факт опечалит многих людей. По-своему он прекрасен, и к нему уже все привыкли.

Времена жизни

Имена времен жизни в Rust указываются с префиксом в виде одинарной кавычки: 'name. Не всем это нравится, но данный синтаксис самый лаконичный из всего, что было предложено. До его введения использовался синтаксис /&name и &name/, который гораздо хуже читается. Идея синтаксиса 'name была навеяна тем фактом, что близким аналогом параметра времени жизни является параметр типа, то есть указание времени жизни по-факту делает элемент обобщенным:

struct StringReader<'a> {     value: &'a str,     count: uint }

Идентификаторы с префиксом или суффиксом в виде одинарной кавычки используются в языках семейства ML. В частности, в OCaml запись 'name используется для обозначения переменной типа в обобщенных конструкциях. Rust продолжает эту традицию.

Кроме того, в Rust нашлось еще одно интересное применение подобного синтаксиса — для задания меток:

'outer: loop {     loop {         break 'outer;     } }

Параллели с временами жизни тут не случайны: предполагалось, что в будущем в языке появится возможность аннотировать любые блоки подобными метками, которые будут также служить идентификаторами времен жизни их содержимого:

'a: {     let x: &'a T = ...; }

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

Макросы

Синтаксис описания декларативных макросов справедливо критикуют за плохую читаемость, хотя сам по себе он довольно простой и понятный. Требование смешения обычного кода с командами макроса налагает серьезные ограничения на синтаксис команд и не в пользу повышения их читаемости. Проблема эта известна, и довольно давно уже ведутся работы по улучшению системы макросов в рамках реализации "macros 2.0". Но пока приходится довольствоваться тем, что есть. Хотя синтаксис макросов действительно часто выглядит токсично, спасает то, что он крайне минималистичен и прост: имеются идентификаторы фрагментов $name и конструкции повторения $(..)+, $(..)* и $(..)?. По сути — это все.

Отдельно стоит упомянуть синтаксис обращения к макросу name!. Он проектировался с целью сделать имена макросов заметнее в потоке кода, чтобы визуально можно было сразу отличить вызов макроса от вызова обычной функции. Это важно, так как макросы расширяют синтаксис языка и принимают в качестве параметра код с пользовательским синтаксисом, то есть синтаксис самого выражения, а не его вычисленное значение. Восклицательный знак хорошо справляется со своей задачей, вызов становится заметным, но не настолько, чтобы перетянуть на себя все внимание (как происходит с другими, широкими символами, вроде @). Интуитивно восклицательный знак можно воспринимать как команду активного действия: макрос разворачивается во время компиляции, тогда как обычная функция остается в этом отношении пассивной. В похожем отношении восклицательный знак также используется в языке D при инстанциации шаблонов.

Собираем все вместе

Хорошо, допустим, каждый из элементов синтаксиса относительно неплох, но как они сочетаются все вместе? Разве их комбинация не превращает Rust-код в нечитаемое месиво из спецсимволов? Например:

fn foo<'a, T: FromStr, I: IntoIterator<Item = T>, F: Fn(T) -> bool>(     self: &'a Self,     first: T,     callable: F,     iter: I, ) -> Result<&'a T, T::Err> {     // ... }

На самом деле это действительно большая проблема. Сигнатуры обобщенных функций с заданными ограничениями, видимо, самые перегруженные синтаксисом элементы языка Rust. И чтобы справиться с ними, Rust предлагает различные синтаксические улучшения и особый сахар, который помогает лучше упорядочить ограничения и скрыть из виду те аспекты, которые и так достаточно очевидны:

fn foo<T, I>(&self, first: T, callable: impl Fn(T) -> bool, iter: I) -> MyResult<T> where     T: FromStr,     I: IntoIterator<Item = T>, {     // ... }

Сигнатура метода foo уже не выглядит настолько страшно, как в исходном варианте. Она не идеальна, но сравнивая с тем, что было, все же заметен явный прогресс в улучшении читаемости. Видно, что алиасы, автовывод времен жизни, сахар для указания self, impl Trait в позиции аргумента, блок where и прочее, введены в язык не случайно и они сильно упрощают чтение действительно сложных мест, с непростой семантикой.

Заключение

Как видите, синтаксис Rust не так уж и плох. По крайней мере любой элемент синтаксиса продуман глубже, чем кажется при поверхностном взгляде и выбран именно таким по веским причинам соблюдения баланса простоты, единообразия и выразительности. Отчего же столько негодования и яростных воплей разносится по сети, насчет "токсичности" синтаксиса Rust? Я думаю главной причиной является объективная сложность и непривычность концепций, которые скрываются за этим синтаксисом, а также высокая информативная плотность кода на Rust. Новичку это вселяет ужас, а опытному Rust-программисту облегчает жизнь, так как расширяет его возможности, обеспечивает высокую степень явности и локализованности кода.

Источники

ссылка на оригинал статьи https://habr.com/ru/post/532660/

Первым в двадцать первый

Я достаю из широких штанин
Рейтинг
И хвастаюсь гордо:
«Смотрите, завидуйте,
Номер один,
А не просто айтишная морда».

Третий год подряд мы заканчиваем на первом месте среди компаний на Хабре. Это не гонка «Формулы 1», это скорее наши «24 часа Ле-Мана», когда на предельном ресурсе и в самых неприятных условиях ты вынужден не останавливаться сутки. Только у нас это не 24 часа, а 24/7/366. И даже сейчас, за несколько часов до Нового года мы пишем этот пост, чтобы не просто рассказать о рекордах, но и немного рассказать о том, как к этому приблизиться.


Мы тут не без самоиронии 🙂

Все расчёты в этой публикации основаны на анализе CSV-файла с информацией обо всех постах блога, который можно экспортировать в панели управления корпоративного блога — спасибо Хабру за такую возможность.

Супер рекорд и пара советов

В прошлогоднем посте мы так оформили статистику:

2016. В блоге опубликовано 72 поста — 58 (80%) из которых были с положительным рейтингом и 14 (20%) с отрицательным.

2017. В блоге опубликовано 170 постов, из которых с отрицательным рейтингом лишь одна (0.58%).

2018. Опубликовано 260 постов, из которых с положительным рейтингом были все 260.

2019. Голосом Дудя: Триста. восемь. постов. 308.

2020. 621 статья. Голоса Дудя не будет, будет картинка:

ААААААА!!!!!!!!

Практически весь год мы были лидером в рейтинге на Хабре — в новый 2021 год мы входим также с 1 местом. Поддерживать такой уровень — огромный труд нашей внутренней редакции, наших сотрудников и наших авторов. Представьте себе: 621 публикация — и это помимо нескольких масштабных спецпроектов, открытий новых дата-центров, полного рефакторинга саппорта и, конечно, круглосуточного труда по обеспечению бесперебойной устойчивой работы инфраструктуры наших клиентов. 

Не секрет, что в бизнес-сфере можно купить первые места в некоторых рейтингах — именно для этого они и создаются. Первое место на Хабре — не продаётся, не читерится, не хакается, не накручивается скриптом. Его можно только заслужить благодаря вниманию и интересу аудитории и здесь нет какого-то универсального рецепта типа «пишите каждый день по 2 статьи и рейтинг будет наивысшим». Нет, для достижения такого результата должны совпадать периодичность публикаций и их качество (состоящее из просмотров, рейтинга, добавлений в избранное и, пожалуй, магии).

Вот вам несколько полезных советов.

  • Готовьте контент заранее: у вас в запасе должно быть всегда несколько статей, чтобы никакие события не влияли на периодичность выхода (так вы страхуетесь от сорванных авторами дедлайнов, больничных сотрудников и прочих факапов).
  • Работайте по контент-плану: рассчитывайте тематики публикаций, создавайте циклы, соотнесите дни недели и содержание статей.
  • Чётко обозначьте цели блога на Хабре (их может быть несколько) и формируйте контент под эти цели.
  • Не «забивайте» статьи рекламой и не превращайте их в расширенные пресс-релизы: хороший контент приводит к достижению целей эффективнее, чем куча активных ссылок с метками.
  • Используйте ресурс своих сотрудников: никто не напишет о компании, о её продуктах и проектах с большим пониманием и большей глубиной, чем сотрудник, который в этом всём варится и который причастен к этому.
  • Прислушивайтесь к аудитории: из комментариев вы можете легко понять, что нравится читателям, а что — нет.
  • Выберите несколько тем, в которых вы готовы стать экспертами или найти выдающихся экспертов: такие статьи не только принесут рост рейтинга, но и сыграют роль в формировании имиджа компании.

Не корпоративных авторов это тоже касается, правила довольно универсальны.

Курс на уникальность

В 2020 мы увеличили долю авторских статей и уменьшили долю переводов. Кроме того, источником интересного контента для нашего блога стали спецпроекты (о них мы уже писали в обзорной статье — посмотрите, если что-то пропустили). Публикации, написанные в рамках спецпроектов, были не только обычными постами на Хабре, но и способом информировать аудиторию, обсудить с ней ход каждого проекта, вовлечь её. И здесь мы получили ситуацию win-win: пользователи Хабра принимали участие в спецпроектах, узнавали что-то новое, а мы получали активный фидбэк. 

Минимальная оценка публикации в блоге RUVDS в этом году +1, максимальная — +280. В среднем за пост мы получали +39, наиболее распространённое значение +33. Чаще всего к нашим статьям ставили всего 1 минус, а в среднем 4 минусов, при этом самый заминусованный пост набрал аж минус 64. Это была публикация «Горячая четвёрка умирающих языков программирования», которая оказалась очень дискуссионной и, как и любая спорная история, набрала без малого 90 тыс. просмотров, 43 минуса и почти 60 тыс. просмотров набрала, например, ещё статья «Я перехожу на JavaScript», собравшая 409 комментариев. Такие публикации — пример того, что не стоит бояться минусов, важно понимать, насколько дискуссионна и интересна статья, умы и души скольких читателей она задела. Вообще, по своему опыту скажем, что показатели Хабра исключительно как цифры — негодный KPI (хотя из разговоров с другими коллегами мы знаем, что именно так их активно используют). Хабр — история практически вне KPI, так как нужно оценивать общий, интегральный эффект, а в нём десятки особенностей и зависимостей.

Мы понимали, что обстановка 2020 года не пройдёт даром ни для Хабра, ни для нас на Хабре — более того, сложно было даже спрогнозировать, упадут просмотры (все в панике и депрессии) или вырастут (все начали читать из дома). Активная работа над контент-планом стала для нас путём к стабилизации блога и триггером новых рекордов. Полученный эффект стоил каждого нейрона и импульса, затраченного на работу с блогом.

В сравнении с предыдущими годами:

2016 2017 2018 2019 2020
Постов опубликовано

С положительным рейтингом

С отрицательным рейтингом

72

58 (80%)

14 (20%)

170

169 (99.4%)

1 (0.6%)

260

260 (100%)

0

308

308 (100%)

0

621

621 (100%)

0

Всего голосов

Из них плюсов

Из них минусов

Суммарный рейтинг всех постов:

1681 (~23/пост)

1235 (73.5%)

446 (26.5%)

+789 (~11/пост)

5644 (~33/пост)

4794 (85%)

850 (15%)

+3880 (~23/пост)

8639 (~33/пост)

7580 (87.8%)

1059 (12.2%)

+6521 (~25/пост)

13232 (~43/пост)

11870 (89.7%)

1362 (10.3%)

+10508 (~34/пост)

30069 (~48/пост)

27034 (89,9%)

3035 (10%)

+23999 (~39/пост)

Всего комментариев к постам 1919 (~27/пост) 4908 (~29/пост) 5255 (~20/пост) 7863 (~25/пост) 21783 (~35/пост)
Всего добавлений в закладки 5575
(~77/пост)
27236
(~160/пост)
36182
(~139/пост)
36361
(~118/пост)
62253
(~100/пост)
Всего просмотров постов 1238367
(~17299/пост)
3547173
(~20865/пост)
4247966
(~16338/пост)
4973912
(~16149/пост)
9939699
(~16006/пост)


В этом году было очень много разнообразных хабов, поэтому мы остановились на 10 основных, в которых размещали свои публикации:

В прошлом году топ был таким:

Вы можете видеть, насколько выросло многообразием тем и как количественно изменились показатели для хабов. Действительно, в этом году карьера в IT значительно больше волновала аудиторию и в блоге не могло не случиться ответа на этот запрос. Серьёзно выросла доля публикаций, связанных с системным администрированием и это тоже закономерно: сейчас эта сфера переживает самую значительную и активную трансформацию, она буквально возрождается в новых качествах DevOps, DevSecOps и т.д. и, думаем, 2021—2022 год принесут очень большой рост «нового сисадминства», приведут компании к условиям принятия ITSM в том числе, видоизменят все основы ITSM и ITIL. Так что немного побудем видионерами и призовём прокачивать скиллы сетевого и серверного администрирования, скоро рынок сформирует запрос на крутых специалистов. 

Топ публикаций: есть что почитать на праздниках

Как мы уже говорили выше, мы изменили подход к качеству контента: у нас стало много авторских, уникальных статей, для работы над которыми мы с радостью привлекаем экспертов и авторов Хабра (например, три из 10 самых рейтинговых публикаций принадлежат перу клавиатуре Павла Zhovner, которого, кстати, мы поздравляем с главной сенсацией этого года — его фееричным выходом на Kickstarter с проектом Flipper Zero). Если вы тоже автор или крутой эксперт или у вас есть коварный замысел по захвату Хабра своими статьями, срочно листайте до конца — там для вас есть информация.

Ещё одним неплохим решением этого года было создание двух циклов статей:

  1. «Профессия» (сисадмин, тестировщик, программист, бэкенд-разработчик, фротенд-разработчик, мобильный разработчик) — о том, что из себя представляют профессии в ИТ и как в них попасть самым коротким путём.
  2. «Новичкам фондового рынка» (здесь внутри ссылка на все 4 статьи цикла) — «разжевательно»-базовые статьи по темам биржевого дела и работы с фондовыми рынками.

Такие циклы несут более социальную функцию, образовательную. Это уникальные авторские материалы, которые помогут не одному десятку начинающих и учащихся.

В целом топы наших публикаций за 2020 год выглядят так. Если прошли мимо, обязательно почитайте в праздники: там что тексты, что комментарии, всё огонь. 

Топ-10 публикаций нашего блога по рейтингу

Топ-10 публикаций нашего блога по просмотрам

Топ-10 публикаций нашего блога по комментариям

Топ-10 публикаций нашего блога по добавлению в избранное

Обратите внимание, насколько однороден топ по комментариям, рейтингу и просмотрам и как от него отличается топ по добавлению в избранное: в нём находятся полезные публикации, которые могут выступать учебными материалами и использоваться в работе. Просмотры таких статей обычно растут и за пределами суток «на главной». Важно чередовать контент разного уровня сложности и полезности — тогда вас полюбит самая широкая аудитория.

Кстати, об аудитории: в этом году мы получили 13,5 тыс. новых подписчиков — отчасти это заслуга нашего контента, но по большей части заслуга онбординга новых пользователей Хабра. Важно, что вы с нами и все эти оценки в виде рейтинга статей, комментариев, добавлений в избранное — ни что иное как оценка наших читателей, самых бдительных и строгих критиков.

В прошлом году мы с оптимизмом смотрели в 2020 и в плане работы он получился действительно довольно напряжённым и результативным, чего не скажешь об общей обстановке. Загадывать на 2021 сейчас не берётся никто, это буквально чёрный ящик с точки зрения рационального прогнозирования, поэтому мы лишь скажем, что будет много статей, спецпроектов, конкурсов, крутых интервью. Поэтому мы ждём от вас две важные вещи:

  • вашу критику, ваши замечания и предложения по развитию нашего блога: чего вам не хватает, какие темы вы бы хотели прочитать (это может быть не обязательно хранение данных и серверное администрирование);
  • вашу помощь: если вам есть что рассказать в рамках нашего блога, вы готовы делиться своим опытом и создавать крутые и интересные материалы, если у вас есть идеи постов и спецпроектов, пишите нам по всем каналам (включая личку Хабра), мы обязательно найдём общий язык и общие выгоды.

Ну что ж, контент-план на начало 2021 года уже готов, мы продолжаем находиться рядом с вами и открыты к любому диалогу. Но вот прямо сейчас хочется открыть уже приготовленное шампанское, достать из холодильника всё, что «не трогай, это на новый год» и немного выдохнуть. Сейчас модно поздравлять с «отступающим 2020» и в этом есть рациональное зерно: это ралли было слишком перегруженным, highload год. Желаем вам встретить 2021 в хорошем настроении и начать его с пользой и в предвкушении радостных событий: это первый год нового десятилетия, которое подарит нам новые вызовы, новые технологии и ещё более безграничные возможности. А это как минимум интересно.

С Новым годом, друзья! 

ссылка на оригинал статьи https://habr.com/ru/company/ruvds/blog/535812/

Как я ушел на 1с-фриланс

Хочу рассказать, как я умудрился уйти на фриланс.

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

Я приближался к возрасту 45 лет, работа программистом 1С вызывала отвращение. Необходимость каждый день быть в офисе «от звонка до звонка» воспринималась как рабство. 1С, кстати, я занимаюсь уже 20 лет. При этом еще 2.5 часа в день тратил на дорогу.

Я подумал и решил, что хочу уйти на фриланс. Но сразу уходить было страшно, поэтому предложил начальству работать на полставки 3 дня в неделю. В свободные 2 дня я планировал отдыхать и искать подработки, которые заложили бы почву моему будущему фрилансу.

Как ни странно, меня не уволили после таких предложений, а предложили, пока не завершится проект у клиента, поработать 4 дня в неделю, а потом уже работать на трехдневке. Дело в том, что на проекте я работал у клиента каждый день. Клиент нормально воспринял мой дополнительный выходной, уж не помню сейчас, когда он был — в понедельник или пятницу.

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

Мой коуч из Goldcoach также помог мне самому принять решение об уходе на одной из коуч-сессии.

К тому же, работа с «жирным клиентом» подходила к концу и я в плане денег видел перспективу падения с 150.000 в месяц до 120.000.

Тяжело было это сделать, но я сообщил начальству, что хочу полностью уйти на фриланс. Сначала мне пеняли на то, что я обещал работать три дня. Но я ведь не раб, могу и уволиться, не так ли? Потом мне предлагали удаленку. Странно, я три года работал в офисе, а удаленку предложили только сейчас. Но я уже твердо был намерен работать на себя.

Этой уверенности помогло еще то, что один мой знакомый по 1С, когда узнал, что я планирую уход на фриланс, предложил мне некоторый объем задач.

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

Для работы я выбрал ставку 1500 рублей в час. Некоторое время работал за нал, но через пару месяцев открыл, как и хотел, ИП и брал уже 1650 рублей в час по безналу. Это по сути те же 1500, но за вычетом 6-7% налогов по «УСН доход». ИП мне открыли бесплатно от банка, где я открывал счет.

Когда я работал во франче, ко мне иногда обращались клиенты по моим разработкам на Инфостарте. Сначала они хотели что-то докрутить в разработках под себя, потом просили помочь с текущими задачами. Но, работая в фирме, я не очень часто откликался на их просьбы, потому что был загружен работой и на «шабашки» не оставалось сил.

Но тут я сообщил им, что свободен и от них пришло много работ.

Меня как раз попросили внедрить маркировку обуви. Я не очень в ней разбирался, но освоился. В итоге даже написал пару статей на Инфостарте по маркировке, с которых ко мне опять пришли клиенты.

Первое время я действовал по классике, размещая объявления на kwork и Юле. С Юлы получил одного постоянного клиента и несколько разовых. С kwork клиенты приходили даже лучше, настолько хорошо, что я со временем сменил там статус на «занят», потому что не мог брать больше работ.

По сути, я начал заниматься фрилансом в декабре 2019 и на 2020 год у меня была поставлена задача добиться стабильности в этом бизнесе. Задача была выполнена, несмотря на пандемию. Забавно, что я опередив многих, ушел на удаленку, пока это еще не «стало модным».

В конце года я посмотрел несколько выступлений Ильи Леонтьева на Youtube. На самом деле по 1С там не очень много спикеров. И вот у него я услышал, что он берет 2500 в час. С тех пор это стало моей целью. На своем сайте я поднял ставку до 1800 рублей в час и новым клиентам озвучиваю ее. Правда, новых пока не прибавилось — не искал, хватает работы от старых.

Но непременно буду брать новых, пока не добьюсь справедливой для своих компетенций и уровня знаний ставки в 2500 рублей в час.

Вот так я ушел на фриланс из офиса.

Оказалось, что проблема была не в том, что программирование на 1С мне опостылело, а в том, что мне опостылел формат офисной работы.

Теперь я работаю 4 часа в день, 5 дней в неделю. И мне хватает, получаю не меньше, чем раньше.

Во франчайзи я закрывал обычно 6 часов в день, за 20 рабочих дней это 120 часов. По ставке 1000 рублей в час получал 120.000 рублей. Я работал на почасовке, потому что это выгодно, чаще получалось больше.

Как вы понимаете, сейчас на фрилансе я работая 4 часа в день, закрываю 80 часов в месяц. По ставке 1.500 это 120.000. Те же деньги, что и во франчайзи.

Если бы у меня было желание «заработать все деньги мира», я мог бы работать и 6 и 8 часов в день. Но я вижу оставляю это время для стратегического обдумывания своего будущего. Вижу перспективу не только в «работе за часы», но и в так называемом «пассивном фрилансе», когда я делаю разработки и продаю их через Инфостарт или сам.

Благо, фриланс в 2020 году подарил мне относительную свободу и удовольствие от того, чем я занимаюсь. Конечно, я бы предпочел заниматься только блогингом, но он не приносит мне монетизации. Поэтому из двух зол — офис или фриланс я выбираю фриланс, который более-менее мне нравится.

ссылка на оригинал статьи https://habr.com/ru/post/535818/

Как создавался новогодний Хабрачат в этом году

Здравствуйте, коллеги!

Новый год подкрадывается всё ближе, а это значит, что нам снова пора собраться вместе для того чтобы хорошо провести время!

Для тех кто еще не знаком с новогодним хабрачатом, расскажу немного про него. Это несколько чатов на разных платформах, объединенных мостом. Боты в каждой из платформ пересылают сообщения из остальных и получается, что все как-бы находятся в одном чате, при этом не покидаю любимой платформы. В этом чате мы традиционно встречаем Новый год уже четвертый раз (в прошлом году только в одном SSH-чате было больше сотни человек).

В центре внимания, конечно, SSH-чат, который работает только несколько дней до нового года и несколько после. В этом году разные люди так же подняли чаты в XMPP (Jabber), Телеграмме и IRC. Так же традиционно есть чат-бот tars, который может вывести ёлку и поздравить с новым годом. В прошлые года таких ботов делали несколько человек (кто-то даже на баше).

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

Настраиваем SSH-чат

SSH-чат обычно открывает @podivilov, для этого используется кастомный SSH сервер, написанный на go. Устанавливается и настраивается он довольно просто. Для начала можно освободить стандартный порт SSH, чтобы при заходе в наш чат не вводить порт. В ubuntu server, например, это делается так. В файле /etc/ssh/sshd_config находим строку Port 22 и меняем число на нужный порт. После этого перезапускаем ssh демон sudo systemctl restart sshd и стандартный порт свободен (убедитесь, что фаерфол пропустит вас по новому порту).

Скачивается бинарник под нужную платформу из релизов на github. Можно положить его, например, в /usr/bin. Далее пишем простой скрипт, который будет перезапускать сервер, в случае падения.

Скрипт для запуска сервера start.sh
#!/bin/bash  while : do  /usr/bin/ssh-chat --admin=/root/.config/ssh-chat/admins --bind=0.0.0.0:22 --log /root/ssh-chat.log --motd=/root/.config/ssh-chat/motd  sleep 1  done 

Давайте разберем параметры запуска

  • admin путь до файла со списком публичных ключей администраторов (у них стандартный набор возможностей: кик, бан и прочее)

  • bind айпи и порт, на котором сервер будет транслировать

  • log путь до файла с логами чата

  • motd путь до файла с сообщением дня (message of the day), которое будет показываться при каждом заходе в чат

Теперь мы можем запустить этот скрипт любым удобным способом (в сессии tmux/screen, через systemctl и т.д.) и чат готов. Вводим ssh nickname@habr2021.podivilov.ru и попадаем в наш чат. Для списка комманд нужно ввести /help, а для выхода /exit или Ctrl+D.

Настраиваем мост между чатами

Мост уже который год хостит @mrDoctorWho, для этого используется matterbridge, который кстати тоже написан на go. Его установка и настройка, как ни странно происходит точно так же. Скачиваем бинарник из релизов на github и кладем его, куда нам надо. Далее пишем конфигурационный файл и скрипт старта. Как-то так выглядит конфиг от @mrDoctorWho:

Файл конфигурации matterbridge.toml
[xmpp.myxmpp] Server="helldev.net:5222" #Jid your userid Jid="" Password="" Muc="conference.helldev.net" Nick="bridge" RemoteNickFormat="[{PROTOCOL}] &lt;{NICK}> "   [sshchat.mychat] Server="habr2021.podivilov.ru:22" RemoteNickFormat="[{PROTOCOL}] &lt;{NICK}> "  [telegram.mytelegram] Token="" RemoteNickFormat="[{PROTOCOL}] `&lt;{NICK}>` " MessageFormat="Markdown"  [general] MediaDownloadPath="/var/www/" MediaServerDownload="https://helldev.net/"   [[gateway]] name="gateway1" enable=true   [[gateway.inout]] account="telegram.mytelegram" channel="-1001200239638"  [[gateway.inout]] account="sshchat.mychat" channel="sshchat"  [[gateway.inout]] account="xmpp.myxmpp" channel="ny" 

К сожалению, у matterbridge слишком много возможностей, чтобы описать их в этой статье, поэтому советую обратиться к официальной вики, в которой все достоточно понятно и подробно рассписано.

А вот скрипт запуска, который почти ничем не отличается от скрипта для SSH-чата:

Скипт для запуска моста start.sh
#!/bin/bash  while : do  /usr/bin/matterbridge -conf /root/.config/matterbridge/matterbridge.toml  sleep 1  done 

После старта этого скрипта мы имеем полностью работающий мост между всеми нашими чатами.

Создание чат бота

Чат-бот, который гуляет по чатам уже который год подряд, называется tars. Бот поздравляет с Новым годом, выводит новогоднюю ёлку ASCII-артом и имеет несколько других команд, в зависимости от того, кто его делал. Первая версия была запущена три года назад, к сожалению не помню кем. Бот был написан на bash-скрипте с использованием screen.

Свою версию я писал на JavaScript с использованием только стандартных библиотек. С его исходным кодом можно ознакомиться здесь. Если вкратце, он вызвает процесс командной строки с помощью модуля child_process (в зависимости от операционной системы, используются либо bash.exe, либо bash). Далее он открывает ssh сессию может писать либо читать прямо из нее. Кроме этого, код довольно заурядный, с его последней версией вы можете ознакомиться на гитхабе.

Как попасть в чаты

Выбирайте любой понравившийся и присоединяйтесь. С наступающим Новым Годом!

ссылка на оригинал статьи https://habr.com/ru/post/535756/

OPC UA для CPU S7-1200 (FW4.4). Настройка сервера

Начиная с версии 4.4 операционной системы контроллеров линейки S7-1200 появилась возможность опрашивать их по протоколу OPC UA. В настоящий момент времени поддерживается только серверная часть (ПЛК может отвечать на запросы клиентов), клиентская часть — не поддерживается.

В настройке OPC UA сервера на S7-1200 есть отличия от S7-1500 (забегая вперед, скажу, что серверный интерфейс требуется создавать вручную, без этого ПЛК не будет отдавать никаких пользовательских данных, хотя и разрешит входящие подключения).

В первую очередь заходим в свойства и включаем сервер OPC UA.

Не забываем так же указать в настройках, что лицензия на OPC UA была приобретена.

То есть, если не вдаваться в важные тонкости, вроде шифрования трафика и вопросов ограничения доступа, все делаем, как и для S7-1500. Чтобы продемонстрировать ошибочность этого подхода, выполним загрузку CPU прямо сейчас и подпробуем к нему подключиться. В качестве клиента OPC UA применяется та же программа, которая использовалась в примерах работы протокола для линейки S7-1500. Единственное отличие заключается в том, что при установленной на программатор Windows 10 программу-клиент мне приходится запускать с администраторскими полномочиями.

Подключимся к ПЛК.

Сколько ни крути, сколько ни ищи, но никаких переменных пользовательской программы мы сейчас не найдем. Только разного рода вспомогательная информация по серверу.

Связано это с тем, что у нас не создан серверный интерфейс, то есть, не указаны переменные, которые сервер будет отдавать клиенту. В серии S7-1500 этот интерфейс создается по-умолчанию в настройках CPU, и по умолчанию OPC UA сервер отдает все тэги, для которых проставлены разрешения к доступу по OPC UA. Посмотрим скрин-шот настройки серверы для CPU S7-1516

Для 1200ой серии необходимо найти в дереве проекта OPC UA Communications → Server Interfaces и нажать там Add new server interface

Далее перетащим из правой части экрана в левую те тэги, доступ к котором необходимо предоставить по протоколу OPC UA

Выполним компиляцию и загрузим ПЛК. Теперь попробуем подключиться к OPC UA заново. Теперь у нас появился интерфейс Server interface_1 и все заданные в нем переменные.

Значение переменной успешно читается.

ссылка на оригинал статьи https://habr.com/ru/post/535820/