Программист, в основном C++, но интересуюсь всем
46,1
рейтинг
5 января 2012 в 01:19

Разработка → Обзор языка программирования Rust из песочницы

Rust — новый экспериментальный язык программирования, разрабатываемый Mozilla. Язык компилируемый и мультипарадигмальный, позиционируется как альтернатива С/С++, что уже само по себе интересно, так как даже претендентов на конкуренцию не так уж и много. Можно вспомнить D Вальтера Брайта или Go от Google.
В Rust поддерживаются функицональное, параллельное, процедурное и объектно-ориентированное программирование, т.е. почти весь спектр реально используемых в прикладном программировании парадигм.

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



Первое впечатление


Синтаксис языка строится в традиционном си-подобном стиле (что не может не радовать, так как это уже стандарт де-факто). Естественно, всем известные ошибки дизайна С/С++ учтены.
Традиционный Hello World выглядит так:
use std;
fn main(args: [str]) {
    std::io::println("hello world from " + args[0] + "!");
}


Пример чуть посложнее — функция расчета факториала:

fn fac(n: int) -> int {
    let result = 1, i = 1;
    while i <= n {
        result *= i;
        i += 1;
    }
    ret result;
}


Как видно из примера, функции объявляются в «функциональном» стиле (такой стиль имеет некоторые преимущества перед традиционным «int fac(int n)»). Видим автоматический вывод типов (ключевое слово let), отсутствие круглых скобок у аргумента while (аналогично Go). Еще сразу бросается в глаза компактность ключевых слов. Создатели Rust дейтсвительно целенаправленно сделали все ключевые слова как можно более короткими, и, скажу честно, мне это нравится.

Мелкие, но интересные синтаксические особенности


  • В числовые константы можно вставлять подчеркивания. Удобная штука, сейчас эту возможность добавляют во многие новые языки.
    0xffff_ffff_ffff_ffff_ffff_ffff
  • Двоичные константы. Конечно, настоящий программист должен преобразовывать bin в hex в уме, но ведь так удобнее! 0b1111_1111_1001_0000
  • Тела любых операторов (даже состоящие из единственного выражения) должны быть обязательно заключены в фигурные скобки. К примеру, в Си можно было написать if(x>0) foo();, в Rust нужно обязательно поставить фигурнные скобки вокруг foo()
  • Зато аргументы операторов if, while и подобных не нужно заключать в кругные скобки
  • во многих случаях блоки кода могут рассматриваться как выражения. В частности, возможно например такое:
    let x = if the_stars_align() { 4 }
            else if something_else() { 3 }
            else { 0 };
  • синтаксис объявления функций — сначала ключевое слово fn, затем список аргументов, тип аргумента указывается после имени, затем, если функция возвращает значение — стрелочка "->" и тип возвращаемого значения
  • аналогичным образом объявляются переменные: ключевое слово let, имя переменной, после переменной можно через двоеточие уточнить тип, и затем — присвоить начальное значение.
    let count: int = 5;
  • по умолчанию все переменные неизменяемые; для объявления изменяемых переменных используется ключевое слово mutable.
  • имена базовых типов — самые компактные из всех, которые мне встречались: i8, i16, i32, i64, u8, u16, u32, u64,f32, f64
  • как уже было сказано выше, поддерживается автоматический вывод типов

В языке присутствую встроенные средства отладки программ:
Ключевое слово fail завершает текущий процесс
Ключевое слово log выводит любое выражение языка в лог (например, в stderr)
Ключевое слово assert проверяет выражение, и если оно ложно, завершает текущий процесс
Ключевое слово note позволяет вывести дополнительную инфорацию в случае аварийного завершения процесса.

Типы данных


Rust, подобно Go, поддерживает структурную типизацию (хотя, по утверждению авторов, языки развивались независимо, так что это влияние их общих предшественников — Alef, Limbo и т.д.). Что такое структурная типизация? Например, у вас в каком-то файле объявлена структура (или, в терминологии Rust, «запись»)
type point = {x: float, y: float};
Вы можете объявить кучу переменных и функций с типами аргументов «point». Затем, где-нибудь в другом месте, вы можете объявить какую-нибудь другую структуру, например
type MySuperPoint = {x: float, y: float};
и переменные этого типа будут полностью совместимы с переменными типа point.

В противоположность этому, номинативная типизация, принятая в С, С++,C# и Java таких конструкций не допускает. При номинативной типизации каждая структура — это уникальный тип, по умолчанию несовместимый с другими типами.

Структуры в Rust называются «записи» (record). Также имеются кортежи — это те же записи, но с безымянными полями. Элементы кортежа, в отличие от элементов записи, не могут быть изменяемыми.

Имеются вектора — в чем-то подобные обычным массивам, а в чем-то — типу std::vector из stl. При инициализации списком используются квадратные скобки, а не фигурные как в С/С++

let myvec = [1, 2, 3, 4];


Вектор, тем ни менее — динамическая структура данных, в частности, вектора поддерживают конкатенацию.

let v: mutable [int] = [1, 2, 3];
v += [4, 5, 6];


Есть шаблоны. Их синтаксис вполне логичен, без нагромождений «template» из С++. Поддерживаются шаблоны функций и типов данных.

fn for_rev<T>(v: [T], act: block(T)) {
    let i = std::vec::len(v);
    while i > 0u {
        i -= 1u;
        act(v[i]);
    }
}

type circular_buf<T> = {start: uint,
                        end: uint,
                        buf: [mutable T]};


Язык поддерживает так называемые теги. Это не что иное, как union из Си, с дополнительным полем — кодом используемого варианта (то есть нечто общее между объединением и перечислением). Или, с точки зрения теории — алгебраический тип данных.

tag shape {
    circle(point, float);
    rectangle(point, point);
}


В простейшем случае тег идентичен перечислению:

tag animal {
       dog;
       cat;
     }
let a: animal = dog;
a = cat;

В более сложных случаях каждый элемент «перечисления» — самостоятельная структура, имеющая свой «конструктор».
Еще интересный пример — рекурсивная структура, с помощью которой задается объект типа «список»:
tag list<T> {
       nil;
       cons(T, @list<T>);
     }
let a: list<int> = cons(10, @cons(12, @nil));

Теги могут участвовать в выражениях сопоставления с образцом, которые могут быть достаточно сложными.
alt x {
         cons(a, @cons(b, _)) {
             process_pair(a,b);
         }
         cons(10, _) {
             process_ten();
         }
         _ {
             fail;
         }
     }


Сопоставление с образцом (pattern matching)


Для начала можно рассматривать паттерн матчинг как улучшенный switch. Используется ключевое слово alt, после которого следует анализируемое выражение, а затем в теле оператора — паттерны и действия в случае совпадения с паттернами.
alt my_number {
  0       { std::io::println("zero"); }
  1 | 2   { std::io::println("one or two"); }
  3 to 10 { std::io::println("three to ten"); }
  _       { std::io::println("something else"); }
}

В качестве «паттеронов» можно использовать не только константы (как в Си), но и более сложные выражения — переменные, кортежи, диапазоны, типы, символы-заполнители (placeholders, '_'). Можно прописывать дополнительные условия с помощью оператора when, следующего сразу за паттерном. Существует специальный вариант оператора для матчинга типов. Такое возможно, поскольку в языке присутствует универсальный вариантный тип any, объекты которого могут содержать значения любого типа.

Указатели. Кроме обычных «сишных» указателей, в Rust поддерживаются специальные «умные» указатели со встроенным подсчетом ссылок — разделяемые (Shared boxes) и уникальные (Unique boxes). Они в чем-то подобны shared_ptr и unique_ptr из С++. Они имеют свой синтаксис: @ для разделяемых и ~ для уникальных. Для уникальных указателей вместо копирования существует специальная операция — перемещение:
let x = ~10;
let y <- x;

после такого перемещения указатель x деинициализируется.

Замыкания, частичное применение, итераторы


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

1. Ключевое слово lambda используется для объявления вложенной функции или функционального типа данных.

fn make_plus_function(x: int) -> lambda(int) -> int {
    lambda(y: int) -> int { x + y }
}
let plus_two = make_plus_function(2);
assert plus_two(3) == 5;


В этом примере мы имеем функцию make_plus_function, принимающую один аргумент «x» типа int и возвращающую функцию типа «int->int» (здесь lambda — ключевое слово). В теле функции описывается эта самая фунция. Немного сбивает с толку отсутствие оператора «return», впрочем, для ФП это обычное дело.

2. Ключевое слово block используется для объявления функционального типа — аргумента функции, в качестве которого можно подставить нечто, похожее на блок обычного кода.
fn map_int(f: block(int) -> int, vec: [int]) -> [int] {
    let result = [];
    for i in vec { result += [f(i)]; }
    ret result;
}
map_int({|x| x + 1 }, [1, 2, 3]);


Здесь мы имеем функцию, на вход которой подается блок — по сути лямбда-функция типа «int->int», и вектор типа int (о синтаксисе векторов далее). Сам «блок» в вызывающем коде записыавется с помощью несколько необычного синтаксиса {|x| x + 1 }. Лично мне больше нравятся лямбды в C#, символ | упорно воспринимается как битовое ИЛИ (которое, кстати, в Rust также есть, как и все старые добные сишные операции).

3. Частичное применение — это создание функции на основе другой функции с большим количеством аргументов путем указания значений некоторых аргументов этой другой функции. Для этого используется ключевое слово bind и символ-заполнитель "_":

let daynum = bind std::vec::position(_, ["mo", "tu", "we", "do", "fr", "sa", "su"])


Чтобы было понятнее, скажу сразу, что такое можно сделать на обычном Си путем создания простейшей обертки, как-то так:
const char* daynum (int i) { const char *s ={"mo", "tu", "we", "do", "fr", "sa", "su"}; return s[i]; }

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

Еще пример: объявляется функция add с двумя аргументами int, возвращающая int. Далее объявляется функциональный тип single_param_fn, имеющий один аргумент int и возвращающий int. С помощью bind объявляются два функциональных объекта add4 и add5, построенные на основе функции add, у которой частично заданы аргументы.

fn add(x: int, y: int) -> int {
         ret x + y;
     }
type single_param_fn = fn(int) -> int;

let add4: single_param_fn = bind add(4, _);
let add5: single_param_fn = bind add(_, 5);


Функциональные объекты можно вызывать также, как и обычные функции.
assert (add(4,5) == add4(5));
assert (add(4,5) == add5(4));


4. Чистые функции и предикаты
Чистые (pure) функции — это функции, не имеющие побочных эффектов (в том числе не вызывающие никаких других функций, кроме чистых). Такие функции выдяляются ключевым словом pure.
     pure fn lt_42(x: int) -> bool {
         ret (x < 42);
     }

Предикаты — это чистые (pure) функции, возвращающие тип bool. Такие функции могут использоваться в системе typestate (см. дальше), то есть вызываться на этапе компиляции для различных статических проверок.

Синтаксические макросы
Планируемая фича, но очень полезная. В Rust она пока на стадии начальной разработки.
std::io::println(#fmt("%s is %d", "the answer", 42));

Выражение, аналогичное сишному printf, но выполняющееся во время компиляции (соответственно, все ошибки аргументов выявляются на стадии компиляции). К сожалению, материалов по синтаксическим макросам крайне мало, да и сами они находятся в стадии разработки, но есть надежда что получится что-то типа макросов Nemerle.
Кстати, в отличие от того же Nemerle, решение выделить макросы синтаксически с помощью символа # считаю очень грамотным: макрос — это сущность, очень сильно отличающаяся от функции, и я считаю важным с первого взгляда видеть, где в коде вызываются функции, а где — макросы.

Атрибуты


Концепция, похожая на атрибуты C# (и даже со схожим синтаксисом). За это разработчикам отдельное спасибо. Как и следовало ожидать, атрибуты добавляют метаинформацию к той сущности, которую они аннотируют,
#[cfg(target_os = "win32")]
fn register_win_service() { /* ... */ }

Придуман еще один вариант синтаксиса атрибутов — та же строка, но с точкой с запятой в конце, аннотирует текущий контекст. То есть то, что соответствует ближайшим фигурным скобкам, охватывающим такой атрибут.
fn register_win_service() {
    #[cfg(target_os = "win32")];
    /* ... */
}


Параллельные вычисления


Пожалуй, одна из наиблее интересных частей языка. При этом в tutorial на данный момент не описана вообще:)
Программа на Rust состоит из «дерева задач». Каждая задача имеет функцию входа, собственный стек, средства взаимодействия с другими задачами — каналы для исходящей информации и порты для входящей, и владеет некоторой частью объектов в динамической куче.
Множество задач Rust могут существовать в рамках одного процесса операционной системы. Задачи Rust «легковесные»: каждая задача потребляет меньше памяти чем процесс ОС, и переключение между ними осуществляется быстрее чем переключение между процессами ОС (тут, вероятно, имеются в виду все-же «потоки»).

Задача состоит как минимум из одной функции без аргументов. Запуск задачи осуществляется с помощью функции spawn. Каждая задача может иметь каналы, с помощью которых она передает инфорацию другим задачам. Канал — это специальный шаблонный тип chan, параметризируемый типом данных канала. Например, chan — канал для передачи беззнаковых байтов.
Для передачи в канал используется функция send, первым аргументом которой является канал, а вторым — значение для передачи. Фактически эта функция помещает значение во внутренний буфер канала.
Для приема данных используются порты. Порт — это шаблонный тип port, параметризируемый типом данных порта: port — порт для приема беззнаковых байтов.
Для чтения из портов используется функция recv, аргументом которой является порт, а возвращаемым значением — данные из порта. Чтение блокирует задачу, т.е. если порт пуст, задача переходит в состояние ожидания до тех пор, пока другая задача не отправит на связанный с портом канал данные.
Связывание каналов с портами происходит очень просто — путем инициализации канала портом с помощью ключевого слова chan:
let reqport = port();
let reqchan = chan(reqport);

Несколько каналов могут быть подключены к одному порту, но не наоборот — один канал не может быть подключен одновременно к нескольким портам.

Typestate


Общепринятого перевода на русский понятия «typestate» я так и не нашел, поэтому буду называть это «состояния типов». Суть этой фичи в том, что кроме обычного контроля типов, принятого в статической типизации, возможны дополнительные контекстные проверки на этапе компиляции.
В том или ином виде состояния типов знакомы всем программистам — по сообщениям компилятора «переменная используется без инициализации». Компилятор определяет места, где переменная, в которую ни разу не было записи, используется для чтения, и выдает предупреждение. В более общем виде эта идея выглядит так: у каждого объекта есть набор состояний, которые он может принимать. В каждом состоянии для этого объекта определены допустимые и недопустимые операции. И компилятор может выполнять проверки — допустима ли конкретная операция над объектом в том или ином месте программы. Важно, что эти проверки выполняются на этапе компиляции.

Например, если у нас есть объект типа «файл», то у него может быть состояние «закрыт» и «открыт». И операция чтения из файла недопустима, если файл закрыт. В современных языках обычно функция чтения или бросает исключение, или возвращает код ошибки. Система состояний типов могла бы выявить такую ошибку на этапе компиляции — подобно тому, как компилятор определяет, что операция чтения переменной происходит до любой возможной операции записи, он мог бы определить, что метод «Read», допустимый в состоянии «файл открыт», вызывается до метода «Open», переводящего объект в это состояние.

В Rust существует понятие «предикаты» — специальные функции, не имеющие побочных эффектов и возвращающие тип bool. Такие функции могут использоваться компилятором для вызова на этапе компиляции с целью статических проверок тех или иных условий.

Ограничения (constraints) — это специальные проверки, которые могут выполняться на этапе компиляции. Для этого используется ключевое слово check.
pure fn is_less_than(int a, int b) -< bool {
          ret a < b;
     }
 fn test() {
   let x: int = 10;
   let y: int = 20;
   check is_less_than(x,y);
 }

Предикаты могут «навешиваться» на входные параметры функций таким вот способом:
fn test(int x, int y) : is_less_than(x,y) { ... }


Информации по typestate крайне мало, так что многие моменты пока непонятны, но концепция в любом случае интересная.

На этом все. Вполне возможно, что я все-же пропустил какие-то интересные моменты, но статья и так раздулась. При желании можно уже сейчас собрать компилятор Rust и попробовать поиграться с различными примерами. Информация по сборке приведена на официальном сайте языка.
@NeoCode
карма
50,0
рейтинг 46,1
Программист, в основном C++, но интересуюсь всем
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Спецпроект

Самое читаемое Разработка

Комментарии (71)

  • +6
    Однозначно в «мемориз». Хотя и непривычно после С/С++, но обязательно посмотрю, что вырастет из этого языка.
    P.S. Если уже и ключевые слова сделали более короткими, то почему mutable оставили как есть? Мне одному это режет глаза?
    • +1
      Действительно, смотрится несколько громоздко. Мне пришли в голову сразу три причины:
      1) не смогли придумать достаточно внятное сокращение. Вообще, кое-что показалось заимствованным из венгерской нотации, вероятно поэтому fn, u, i очевидны и прозрачны для многих. Аналогов же предмету обсуждения в ней нет, насколько мне известно. Кстати, я сам не смог подобрать кандидата на замену mutable.
      2) оставили как меру против «злоупотребления» изменяемыми переменными, чтобы сделать их более заметными в коде.
      3) просто руки не дошли.
    • +2
      кроме mutable ещё lambda. Очевидно же: mut и la.

      по умолчанию все переменные неизменяемые; для объявления изменяемых переменных используется ключевое слово mutable.

      Что-то тут не так. В первом же примере переменные return и i изменяются, хотя обе не mutable.
  • +8
    Во многом похож на OCaml.
    • +1
      Двоичные константы и подчеркивания в числах из перла.
      • 0
        Я про семантику, а не про синтаксис.
      • 0
        Ну вообще подчёркивание в константах появилось сильно раньше. Как минимум, оно было в Smalltalk.
  • –3
    Как то не логично обьявлять переменную, а потом её тип. Привыкли что наоборот.
    Тоже самое в функциях, привыкли вначале писал что функция возвращает, а уже потом функцию, тут наоборот.

    А где классы? или я что то пропустил? Планируется ли какая UI библиотека?
    • +4
      Да, непривычно. В наши мозги вбито: «Паскаль плохой, Си хороший». Нет, надо брать хорошие вещи из разных языков, и из всего этого сделать автомат Калашникова, надёжный, неубиенный и быстрый. Ведь и Михаил Тимофеевич не изобретал ни автоспуска, ни поворотного затвора…

      И — фтопку ночные сборки: они должны существовать только чтобы проверить, что наваяли проггеры за день. Ну и для межпроцедурных оптимизаций. А перекомпиляция с «обычным» уровнем оптимизаций должна длиться не более минуты. Причин медленной компиляции Си до хрена, наобум припомню синтаксис LR(1) и хедеры.
      • +1
        Да просто это как то не логично, Ещё если они перенимали си подобный синтаскис зачем нагромоздили этим? std::io::println
        неужели точки уже не в моде? std.io.println() куда удобней и привычней.
        • +4
          При их тяге к краткости — как минимум странно.
        • +1
          Я только после вашего комментария заметил этот момент. В Tcl для разделения областей видимости (namespace) используется двойное двоеточение :: и это для меня теперь привычно. Но когда впервые увидел такую конструкцию, у меня тоже челюсть отвисла.
      • 0
        C как раз по моему компилится быстрее чем С++, из-за ацких шаблонов в С++?
        • 0
          По-моему, и шаблоны можно ускорить, если продумать две вещи. 1) Как закэшировать результаты расшаблонивания (хорошо, но недостаточно сделано в C++11 с его extern template). Всё это сделать легко, если отойти от хедеров, и сложно, если за них держаться. 2) Как хотя бы часть работы по расшаблониванию выполнять один раз при синтаксическом анализе модуля — а не каждый раз, когда потребовалось его расшаблонить.
          • +1
            Шаблоны в D ускорили за счет адекватного синтаксиса: заменили угловые скобки круглыми. Откуда не нужно тонн анализа, что это такое: сравнение, побитовый сдвиг или шаблонные скобки. Откуда быстрее компиляция.
            • +2
              Это, кстати, тоже важно. Забыл — а ведь именно в этом месте синтаксис C++ контекстно-зависимый.
              • +1
                Полезное замечание, спасибо. Я помнил, что грамматика С++ не везде контекстно-свободная, но не помнил в каких местах :)
              • +1
                А кстати, только ли в этом месте? Разве разный смысл даже круглых скобок — не контекстная зависимость, или я где-то не прав?
              • –1
                Синтаксис C++ контекстно-свободный. Если не согласны — приведите ссылку на авторитетный источник или продукцию грамматики языка, где присутствует контекстная зависимость.
                • 0
                  Позвольте ответить на ваш комментарий, хоть он адресован и не мне.

                  D E(F);

                  В зависимости от того, что такое F эта конструкция может означать как объявление функции, так и определение переменной.
                  • –1
                    Разумеется может. В зависимости от того, в каком месте стоят скобки они могут означать вызов функции по аргументам, вызов конструктора или функтора, группировку, а в других языках — формирование кортежа, S-выражения, функции или чего угодно еще. Позвольте заметить, вы привели только тело продукции грамматики. Полностью она выглядела бы так:
                    N -> D E(F)
                    Это обычное правило обычной КС-грамматики. А как обработать активацию данной продукции, вывод нетерминала N — дело семантического анализатора (об этом упоминалось выше). Это не делает язык контекстно-зависимым. Все языки программирования (исключений мне не известно) являются контекстно-свободными, включая C++.
    • 0
      Есть классы. Только упомянуты вскользь.
  • +3
    Напоминает попытку напихать фич Haskell в C++. Есть хорошие идеи, но в целом ничего особенного. Как-то аздражает ключевое слово «fn». Почему бы раз уж let ввели для переменных для функций не сделать его же? Странно.
    • 0
      Мне трудно понять по столь куцему описанию, почему они так решили. Но-по-моему, причины три. 1) Хотели быть поближе к LL(1). 2) Хотели не сразу заворачивать мозги «ездового пса» в функциональный стиль, а потихоньку: «А вот эту задачу можно решить ещё и так, в функциональном стиле!» 3) Хотели захватить всё целиком: и низкоуровневые дебри, в которых неплохо работал Паскаль и отлично — Си, и прикладные программы.
      • +1
        А мне кажется все было не так.
        Как я вижу, сели прогеры в кружочек, взяли C++ и начали думать — что ж в крестах не так, и придумывать, как сделать так.
        Убогий это подход. Язык надо делать с нуля а не допиливать существующие.
        Может, я слишком резкий, но мне не нравится. Слишком похоже на «свой цпп с блэкджеком и сопутствующими».
        • +1
          Нехорошо выразился, допиливать, конечно, надо, но не чужой язык. А новый надо делать с нуля.
        • 0
          Java? C#? Это, видимо, тоже попытки сделать C++ со шлюхами. Причём куда более консервативные.

          По-моему, идеальный язык программирования должен быть пригоден как в роли учебного языка (пока Паскаль в этой роли непревзойдённый), так и для профессионального программирования. Ну и достаточно близким к существующим языкам профессионального программирования, из которых «короли» — естественно, PHP, C++, C#.
          • 0
            Нет. Там другая идеология — managed код, виртуальные машины, обьектная система. Это совершенно отличные от цпп языки.
            • –1
              Что мешает изменить идеологию, но оставить знакомый, полюбившийся синтаксис?
              Что мешает сделать native compiler к той же Java/C#?
              • 0
                Да ничего не мешает, вот только главный смысл Java теряется. Тем более точно есть реализация native compiler под Java и не одна.

                За языками подобными Java/C# будущее, ведь байт-код можно исполнять где-то в облаке, а у клиента остается по сути лишь ОЗУ и примитивный процессор.
  • +3
    Круто. Подумывал про собственный язык программирования, но явно мозгов не хватало, чтобы сделать как надо. Эти товарищи сделали всё, что я хотел, и даже лучше. Будем ждать.
    • +2
      Неужели из порядка 8000 языков программирования Вам не подошел ни один? )
      • 0
        Just for funny!
        Чел правильно делает. Попробует написать хоть один язык — разберётся как работают все остальные.
      • +13
        Думаете почему их так много возникло?
    • +1
      Зачем ждать?! Примите участие! :)

      github.com/graydon/rust

      Это
      1) бесценный опыт
      2) фан
      3) кусочек славы

      =)
  • +3
    По-моему очень похож на Go. И учитывая что Go все еще на раннем этапе развития, в будущее этого языка еще меньше верится. Ну и сложность языка на первый взгляд выше, чем у того же C. Хотя, разработчикам, конечно, удачи пожелать хочется.
    • +3
      Ну не знаю на сколько он похож на Go. У этих языков много общего, но у Go — очень простой синтаксис, никаких наворотов. Я бы сказал что Go — subset от Rust. В Rust такое чувство что засунули все, что нашли в других языках «интересненького», от D у меня такое же впечатление осталось.
      Еще мне очень понравилась идея «все сокращать»: function -> fn, float64 -> f64, но она как-то не доведена до конца…
  • 0
    Библиотеки что-то типа java (namespace is divided into modules: use std;). Но непонятно, можно ли дополнять namespace как cpp и функционал namespace из своего, не подключая его через use. И не нашёл в доке чего-то похожего на классы. Странный язык, непонятно «зачем он?».
  • 0
    Как я понимаю там нет сборки мусора? Интересная ниша для окучивания — тот же Go из-за присутствия GC на совсем низкий уровень не покатит.
    • +1
      Garbage collection: optional, per-task, only «shared» types
  • +6
    Любопытно, что потихоньку поднимается волна «Сделать свой православный С++, только лучше». Вон уже как минимум 3 языка делаются для замены С++. Значит, он уже порядком надоел, и, кто знает, может его эра близка к упадку и закату.
    • +3
      Необходимость замены С++ очевидна уже давно, особенно для любителей С++ — но задача эта ой как не проста. Сделают в итоге хоть один — будет уже замечательно.
      • –2
        Я бы сказал, что тут надо разделять два направления: системное программирование и всё остальное. Для системного программирования, конечно, заменителей не хватает, а вот для всего остального — хоть отбавляй.
  • 0
    А как его установить, и протестировать примеры?
    • 0
      Нашёл!
  • 0
    Блин… ссылка сьелась…
    github.com/graydon/rust/wiki/Getting-started
  • 0
    > во многих случаях блоки кода могут рассматриваться как выражения.

    Убивать. Или всегда выражения или никогда.

    И такие же косяки в большинстве других решений. В общем, решили ввести в C++ блэкджек и шлюх, но здание возвели, а игроков и девиц завозить побоялись.
    • 0
      Вот и я о том же! Ну, насчет никогда вы конечно погорячились, этот момент как раз улучшение текущего положения. В C++ же тоже где как, так и тут, но тут больше выражений. Это хорошо.
    • 0
      Если бы не побоялись, то получился бы энергичный хаскелл. А его бы побоялась целевая аудитория языка.
  • –1
    Заразная болезнь Микрософта «фатальный недостаток — not invented here» переросла в пандемию.
    Сначала Гугль, теперь вот Моцилла. Сколько можно изобретать велосипеды?

    Неужели нельзя взять готовый язык, доработать, довести его до ума, без того, чтобы переписывать гигабайты готового кода?
    • +1
      А какой конкретно надо взять язык? И как можно так глубоко доработать, чтобы не потерять совместимость?

      Чтобы не переписывать тонну кода, вполне достаточно бинарной совместимости.
      • –1
        Как раз таки бинарная совместимость и тащит за собой кучу патентов и прочие проблемы.
        А вот чистый синтаксис + дополнительные языковые примочки могли бы решить многие головные боли разработчика.

        Взять хотя бы тот же Nemerle — вот что мешало делать навороты на базе C#? Людям надо было бы учить только дополнительные плюшки и использовать их в работе. Нет, кому-то надо было погладить своё эго, придумать очередной велосипед с квадратными колесами там, где уже и так всё отлично продумано и работает.

        Мы наш мы новый мир построим — все знают чем это закончилось.
        • +1
          А поясните, пожалуйста, каким образом бинарная совместимость тянет за собой патентные проблемы? Что-то в толк не возьму. Но согласен с jakobz: делать самостоятельный язык на базе другого нельзя. Это получится не самостоятельный язык, а язык + примочки. Кроме того, у всех языков разная философия, и ее уже не изменить.
          • –1
            Ну возьмем к примеру байткод на Гудроидах — Оракл сразу зубами вцепился…

            А по второму вопросу — что мешает взять за базовый синтактис ту же яву или сишарп? Почему каждому начинающему велосипедисту хочется выдумывать свои аналоги var? Зачем курочить устоявшиуюся декларацию функций и порядок типов?

            Чем

            fn lt_42(x: int) -> bool

            лучше чем

            bool lt_42(int x)

            ?

            Столько лишних закорючек — ни за что не поверю, что они вообще о удобстве для программиста задумывались.
            • 0
              Байткод — это не бинарник и к бинарной совместимости имеет весьма отдаленное отношение. Сам по себе он уже является технологией, форматом предварительно скомпилированного кода, если хотите. И байткод для Java принадлежит одной конкретной компании. С другой стороны формат исполняемого файла для любой ОС — тоже кому-то принадлежит (Microsoft, сообщество Linux), но пока MS никого не гнобит за его использование (даешь программ под Windows, — и чем больше, тем лучше). А вот за синтаксис тех же C# и Java в каком-то другом языке корпорация-владелец вполе может и покусать.
            • 0
              «Не бинарник» — имею в виду не исполняемый нативно бинарник. Потому что файл все-таки бинарный, а не какой-нибудь там текстовый.

              По синтаксису: стрелка перед bool в вашем примере имеет под собой некое теоретическое обоснование. Дело в том, что в нескольких ML-подобных языках (Haskell, OCaml, F#) стрелка разделяет типы в типизации выражения. Пример:

              max :: Int -> Int -> Int — Определение типа функции max

              В математике есть понятие отношений, где также используется стрелка: R -> R (если я правильно помню матчасть).
            • +1
              let — стандартное ключевое слово для биндингов в куче языков.
              декларация типа a -> b -> c -> d тоже стандарт.

              а вот bool lt_42(int x) читается как «применить функцию bool к переменной lt_42 и значению выражения „применить функцию int к переменной x“ », что несколько сбивает с толка.
    • 0
      Какой, например, готовый язык?
  • 0
    О, а вот эта концепция мне нравится, в отличие от Go. Typestate — вообще великолепная задумка, надо подумать, как его можно реализовать библиотекой на D :)
    • 0
      Вы имеете ввиду ADT?
      • 0
        ADT?
  • 0
    image
  • 0
    Необычный велосипед…

  • 0
    Я заметил косяк или чего-то не понимаю?
    В характерных чертах отмечена необходимость mutable для изменения переменной. Но пример факториала или кошки-собаки этого не отражают.
  • 0
    Блин, зачем обязательно навязывать эти фигурные скобки? Ну почему нельзя сделать возможность использовать выравнивание?
    • 0
      Затем чтобы некоторые личности имели возможность писать не форматированный код.
  • 0
    Название языка «Ржавчина» — это намек на то чем он станет?
  • 0
    Что-то заявленное стремление к краткости не особо выполняется.
    Синтаксис хаскеля для тех же самых вещей куда компактнее.

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