Комментарии 53
На D можно написать так:
auto add( X, Y )( X x, Y y ) if (__traits(compiles, x + y)) {
(это не самый правильный вариант, но самый простой) и тогда ошибка будет вполне внятной.
a.d(13): Error: template main.add cannot deduce function from argument types !()(int, string), candidates are:
a.d(5): main.add(X, Y)(X x, Y y) if (__traits(compiles, x + y))
P.S. Вобще, сравнение двух языков, да еще с примерами без _отличного_ знания обоих языков напоминает мне «перевожу с японского на сомалийский. Знание обоих языков — читаю со словарём». Извините.
auto add( X, Y )( X x, Y y ) if (__traits(compiles, x + y)) {
(это не самый правильный вариант, но самый простой) и тогда ошибка будет вполне внятной.
a.d(13): Error: template main.add cannot deduce function from argument types !()(int, string), candidates are:
a.d(5): main.add(X, Y)(X x, Y y) if (__traits(compiles, x + y))
P.S. Вобще, сравнение двух языков, да еще с примерами без _отличного_ знания обоих языков напоминает мне «перевожу с японского на сомалийский. Знание обоих языков — читаю со словарём». Извините.
0
Знание обоих языков — читаю со словарём
А можно какие-то аргументы? Хотелось бы думать, что Rust я знаю не так уж плохо.
Для D я брал готовый код и, разумеется, мог упустить какие-то нюансы. Впрочем, подозреваю, что гоферы точно так же не были довольны оригинальной статьёй. (:
На D можно написать так:
В курсе. Речь шла о том, что на Rust нельзя написать без явного указания требований к типам. То есть, в D мы можем написать "быстро и коротко", а можем "правильно". В первом случае, перекладываем проблемы на пользователей. При этом, я признаю, что далеко не всегда нужен такой сильный контроль над типами — шаблон может быть приватным, например. Но опять же, для Rust это последовательное решение в духе языка.
+3
Да хотя бы вот (листнул на первый попавшийся пример)
Вы пишете «В Rust компилятор запрещает обращениe к не инициализированным переменным.». И все. Это как-то выделяет Rust? А D что, разрешает?
Вы пишете «В Rust компилятор запрещает обращениe к не инициализированным переменным.». И все. Это как-то выделяет Rust? А D что, разрешает?
0
Это как-то выделяет Rust? А D что, разрешает?
D, по умолчанию, просто инициализирует все переменные:
int val;
writeln(val); // выведем int.init
Rust этого не делает и требует нас самих инициализировать:
let val: i32;
println!("{}", val); // use of possibly uninitialized variable
Согласитесь, что есть разница. Да, в плане "безопасности" она не особо существенна, но подход-то другой.
Ну и если честно, не совсем понял аргумент. Как процитированное относится к знанию языка? Я разве что-то неправильно сказал?
+2
Для языка с таким небольшим сообществом D поражает своими возможностями. Многое, что только ползёт в С++ (или напротив, ещё и не планирует), здесь уже есть: ranges, modules, template constraints (~concepts), contracts, unit tests, scope guards*. При этом сочетается всё как-то более гармонично, чем в C++. Меньше базовых принципов что ли.
Жаль, что работодателей мало интересуют D и Rust. Они разные, но выглядят очень интересно оба.
* Про последние не так давно узнал, к слову. На первый взгляд, удобнейшие возможности. Есть выступление Александреску на тему поддержки C++:
Жаль, что работодателей мало интересуют D и Rust. Они разные, но выглядят очень интересно оба.
* Про последние не так давно узнал, к слову. На первый взгляд, удобнейшие возможности. Есть выступление Александреску на тему поддержки C++:
Скрытый текст
0
Точку с запятой в этом примере можно и опустить — она используeтся, чтобы превратить выражения (expressions) в инструкции (statements), а так как и main и println! ничего не возвращают (на самом деле, возвращается специальный пустой тип ()), то разницы нет.А как всё же реализовать несколько точек выхода? Типа такого:
```d
int getHash( string str )
{
if( string.length == 0 ) return 0;
// считаем хеш по хитрому алгоритму
return hash;
}
```
Явно указывать название модуля не нужно, если мы не хотим объявить вложенный модуль, так как оно зависит от имени файла или директории.В D на самом деле аналогично.
мы жертвуем лаконичностью и, отчасти, гибкостью ради «явности» и более удобных сообщений об ошибках — из-за необходимости указывать ограничения типам, проблема не может просочиться через много уровней, порождая кучу сообщений.Но в примере как раз наоборот, сообщение в Rust не говорит какой именно параметр мы не так написали (trait Add is not implemented for the type), а вот в D нам не просто сказали, что нет реализации такой сигнатуры (Error: template instance main.add!(int, string) error instantiating), но и подсказали почему (Error: incompatible types for ((x) + (y)): 'int' and 'string').
В Rust компилятор запрещает обращениe к не инициализированным переменным.В D просто нет такого понятия как «неинициированная переменная», так как с ними много проблем. От проблем с безопасностью, до сложностей с побитовым сравнением. Например, структуры в D сравниваются наиболее быстрым способом — через сравнение участков памяти.
Оба языка умеют выводить типы, но в Rust тип может выводиться не только из объявления, но и использования:Очень опасный приём, особенно в свете множественной диспетчеризации.
a as i64Вот, кстати, что это, что `cast(long) a` — одинаково не удобны в выражениях, в отличие от `a.to!long`
-3
А как всё же реализовать несколько точек выхода?
Конечно же, в Rust есть return. Но его использование идиоматично только для ранних возвратов.
Но в примере как раз наоборот, сообщение в Rust не говорит какой именно параметр мы не так написали
На самом деле здесь важны не сообщения об ошибках, а типобезопасность. В отличие от шаблонов в C++ или в D, дженерики в Rust позволяют применять к типам-параметрам только те операции, которые разрешаются ограничениями на эти параметры. В этом плане Rust ближе к языкам типа Java или к концептам (так и не вошедшим в стандарт, емнип) в C++. Ну и сообщение об ошибке на самом деле говорит какой конкретно типовый параметр неправильный, просто автор статьи не привёл это сообщение полностью. Вообще у rustc сообщения об ошибках просто феноменальные.
так как с ними много проблем
Ну в Rust с ними нет проблем, потому что переменные, которым ничего не присвоено, использовать нельзя, компилятор это проверяет статически и способа обойти это нет. То, как в Rust сравниваются объекты, зависит от реализации трейта PartialEq. В случаях, когда этот трейт автоматически выведен через derive (99%), то если структура состоит целиком из примитивных типов, то всё сведётся к побайтовому сравнению.
Очень опасный приём, особенно в свете множественной диспетчеризации
Вообще говоря, вывод типов — это безумно удобная вещь. Вы писали когда-нибудь на Haskell? В отличие от хаскеля, в Rust вывод типов исключительно локальный. Проблем с ним не возникает никогда, а удобства — выше крыши. И если под "множественной диспетчеризацией" вы понимаете перегрузку функций, то её в расте нет, так что этой проблемы не возникает :)
+7
Конечно же, в Rust есть return. Но его использование идиоматично только для ранних возвратов.
Тогда не очень понятно, что они сэкономили сделав return опциональным. Неконсистентный синтаксис получился.
В отличие от шаблонов в C++ или в D, дженерики в Rust позволяют применять к типам-параметрам только те операции, которые разрешаются ограничениями на эти параметры.В D можно делать и так и так. Можно использовать структурную типизацию через шаблоны, а можно номинативную, через интерфейсы.
Ну и сообщение об ошибке на самом деле говорит какой конкретно типовый параметр неправильный, просто автор статьи не привёл это сообщение полностью.Вот полностью из песочницы на сайте:
```rust
error: the trait `core::ops::Add<&str>` is not implemented for the type `_` [E0277]
:2:25: 2:56 note: in this expansion of format_args!
:3:1: 3:54 note: in this expansion of print! (defined in )
```
Как-то не очень понятно.
Ну в Rust с ними нет проблем, потому что переменные, которым ничего не присвоено, использовать нельзя, компилятор это проверяет статически и способа обойти это нет.Но в 99% случаев инициализируются они всё равно дефолтными значениями. Наверняка в процессе хитрых кастов можно случайно обойти ограничение на неиспользование неинициированной памяти.
Вообще говоря, вывод типов — это безумно удобная вещь.Речь не о выводе типов, которая есть и в D, а изменение типа в зависимости от того, какая функция была вызвана первой. Редактирование такого кода, как хождение по минному полю. Ну или покажите пример, где оно действительно полезно.
-4
Тогда не очень понятно, что они сэкономили сделав return опциональным. Неконсистентный синтаксис получился
В большинстве случаев early returns не используются. Кроме того, Rust — это expression-based язык, как Scala. Например, вместо тернарного оператора в нём
if
:let y = if x > 0 { ... } else { ... };
Отсутствие необходимости писать return для возврата значения, если это значение — последнее выражение в функции, вытекает из этого совершенно естественно.
номинативную, через интерфейсы
Интерфейсы — это всё-таки немного не то. Если я не ошибаюсь, в D, как и в C++ и в Java, интерфейсы подразумевают динамическую диспетчеризацию. В Rust динамическая диспетчеризация используется только если вы сами её попросите (через трейт-объекты).
Вот полностью из песочницы на сайте
Такое сообщение появляется, если в качестве параметра
add()
используется нетипизированный литерал (42
). Для удобства в Rust, как и во многих языках, числовые литералы могут автоматически принимать любой тип. Но в данном контексте он используется в качестве аргумента дженериковой функции, поэтому компилятор не может вывести тип литерала так просто. Вот такая ошибка возникает, если тип литерала указать явно (42i32
):<anon>:9:20: 9:23 error: the trait `core::ops::Add<&str>` is not implemented for the type `i32` [E0277]
<anon>:9 println!("{}", add(42i32, "eee"));
^~~
На мой взгляд, понятнее некуда.
Но в 99% случаев инициализируются они всё равно дефолтными значениями. Наверняка в процессе хитрых кастов можно случайно обойти ограничение на неиспользование неинициированной памяти.
Неинициализированные переменные ничем не инициализируются, если этого не указать явно, и нет, случайно обойти это ограничение нельзя в принципе. Одна из целей Rust — это гарантия полной безопасности работы с памятью; это включает в себя полный запрет на работу с неинициализированной памятью вне
unsafe
.изменение типа в зависимости от того, какая функция была вызвана первой
Вообще-то это и есть вывод типов. Компилятор выводит тип переменной в зависимости от того, как она используется. Просто в Rust вывод типов действует внутри функции целиком, а не только при присваивании значения, как с
auto
в C++.Редактирование такого кода, как хождение по минному полю. Ну или покажите пример, где оно действительно полезно.
Вам это так кажется. Редактировать и рефакторить такой код ничуть не сложнее, чем код без вывода типов, даже в чём-то проще — меньше печатать нужно. Почти не бывает такого, чтобы при рефакторинге тип переменной внезапно менялся, хотя бы потому, что объявлять отдельностоящие неинициализированные переменные неидиоматично. Кроме того, объявления типов обязательны в сигнатурах функций и констант/статических переменных, даже если они объявляются внутри другой функции.
Пример "действительной полезности" привести сложно — это всё-таки не фича уровня borrow checker'а, без которой невозможно жить, а элементарное удобство. Обойтись без этого можно всегда. Но я вот совершенно не могу придумать несинтетического примера, где это бы помешало. Специально сейчас пять минут посвятил просмотру кода своих библиотек, и не нашёл ни одного проблемного места.
+5
Отсутствие необходимости писать return для возврата значения, если это значение — последнее выражение в функции, вытекает из этого совершенно естественно.Кажется я понял. Не «точка с запятой не обазательна» или «точка с запятой превращает выражение в утверждение», а просто всё есть выражения, и функция возвращает значение последнего, просто последнее выражение может быть пустым и возвращать соответственно пустоту.
Интерфейсы — это всё-таки немного не то. Если я не ошибаюсь, в D, как и в C++ и в Java, интерфейсы подразумевают динамическую диспетчеризацию.Нет, не подразумевают. И речь не только про интерфейсы классов.
На мой взгляд, понятнее некуда.Ну то есть, приходится отказывать себе в удобстве, чтобы был понятный вывод.
дна из целей Rust — это гарантия полной безопасности работы с памятью; это включает в себя полный запрет на работу с неинициализированной памятью вне unsafe.Ну а в unsafe вы получаете все эти проблемы, что и требовалось доказать :-)
Редактировать и рефакторить такой код ничуть не сложнее, чем код без вывода типов, даже в чём-то проще — меньше печатать нужно.Я всеми руками за выведение типов и каждый день его использую. Речь исключительно о: «в Rust вывод типов действует внутри функции целиком, а не только при присваивании значения».
Пример «действительной полезности» привести сложно — это всё-таки не фича уровня borrow checker'а, без которой невозможно жить, а элементарное удобство.Так покажите это удобство.
-3
Нет, не подразумевают
Что же это за такие интерфейсы?
отказывать себе в удобстве
Всё ещё не понимаю, про какое удобство вы говорите. На мой взгляд, шаблоны по типу C++, хоть и мощнее, значительно сложнее в правильном использовании. Дженерики с ограничениями трейтов проще и практичнее. И удобнее, потому что я точно знаю, когда пишу дженериковую функцию, что она будет работать везде и всегда, какие бы типы в неё не передали.
Ну а в unsafe вы получаете все эти проблемы, что и требовалось доказать :-)
Простите, но это настолько избитая тема, что я уже не хочу на неё отвечать подробно. Кратко — нет, это не то что требовалось доказать, и да, гарантия того, что подобные вещи возможны исключительно в unsafe, это game breaker. Единственный способ получить непроинициализированную переменную — это вызвать unsafe-функцию
mem::uninitialized()
:let x: i32 = unsafe { mem::uninitialized() };
И хотя бы поэтому случайно обратиться к неинициализированной памяти сложно. Особенно если у вас проекте указано
#![forbid(unsafe)]
.Так покажите это удобство.
Я не могу этого сделать, потому что вывод типов используется везде, и в каждом конкретном участке кода вывода типов не очень много, и примеры будут неубедительными. Если же писать код много, удобство становится очень заметным.
+4
Речь не о выводе типов, которая есть и в D, а изменение типа в зависимости от того, какая функция была вызвана первой.
От этого зависит сообщение об ошибке. Если программа компилируется, то тип от перемены порядка вызовов меняться не будет.
0
Редактирование такого кода, как хождение по минному полю. Ну или покажите пример, где оно действительно полезно.
Лучше покажите как при помощи этой фичи можно в ногу неожиданно выстрелить. (:
Впрочем, в Rust есть ещё одна "спорная фича":
let a = 10;
let a = a.to_string();
let a = Some(a);
На всякий случай, уточняю: переменная
a
неизменяемая, просто тут три разных а
. Когда впервые увидел, то показалось, что это ужасное решение и источник непонятных ошибок. А на деле оказывается удобно.0
Я пацифист.
А три разных а — и правда ужасное решение ибо вносит неразбериху. Не стоит оно того сомнительного удобства.
А три разных а — и правда ужасное решение ибо вносит неразбериху. Не стоит оно того сомнительного удобства.
0
и правда ужасное решение ибо вносит неразбериху. Не стоит оно того сомнительного удобства.
А ведь кто-то так думает про шаблоны...
+3
В D порядок объявления шаблонов ни на что не влияет. А тут одна переменная втихую перекрывает другую.
-1
Я не про порядок, а про точку зрения. Или про "парадокс Блаба", если угодно. Гоферы живут без шаблонов и не (особо) жалуются, а вам кажется, что языки без обобщённого программирования заведомо убоги. Так и тут — вы видите источник проблем, а кто-то удобную возможность. Стоит её распробовать перед тем как критиковать.
Собственно, не в расте эту штуку придумали. И ограниченное "скрытие" есть даже в С++.
Собственно, не в расте эту штуку придумали. И ограниченное "скрытие" есть даже в С++.
+5
В JS вполне частая ситуация, когда кому-то лень было придумывать новое имя и он воспользовался существующей переменной. В итоге во второй половине функции нет доступа к затёртому значению. А чтобы его получить нужно разделить имена. Вручную.
0
А чтобы его получить нужно разделить имена. Вручную.
Дык, если нам нужен доступ к переменным, то код мы, очевидно, и так модифицируем. В чём проблема переименовать?
А если код был бы вообще в виде цепочки вызовов, за которую вы так ратуете, записан? Тогда потребовалось бы ещё больше изменений.
+1
Оно досталось внаследство от функциональщины, того же OCaml, насколько я помню.
Плюсы:
Плюсы:
- помогает убрать изменяемость объекта без лишнего блока, иногда очень удобно.
- хорошо сочетается с семантикой перемещения —
let foo = foo.unwrap();
.
- если что-то пошло не так, то ты почти всегда получишь предупреждение о неиспользованной затененной переменной.
- ну и уж если совсем не нравится, то для своего проекта можно clippy подтянуть, в нем три проверки есть (по умолчанию все выключены):
shadow_reuse — rebinding a name to an expression that re-uses the original value, e.g.
let x = x + 1
shadow_same — rebinding a name to itself, e.g.let mut x = &mut x
shadow_unrelated — The name is re-bound without even using the original value
+3
В некоторых языках явно различают let и let req. Отсутствие этого разделения в Haskell меня расстраивает: достаточно типичная ошибка в нем — в блоке let n = 5 in let n1 = n+1 in… можно перепутать переменные n и n1.
Реализация let req в неленивом языке нетривиальна, а в C-подобных языках требуется он достаточно редко. По этому нерекурсивный let с возможностью переопределить переменную зависимым от нее значением очень полезна.
Реализация let req в неленивом языке нетривиальна, а в C-подобных языках требуется он достаточно редко. По этому нерекурсивный let с возможностью переопределить переменную зависимым от нее значением очень полезна.
+2
Тогда не очень понятно, что они сэкономили сделав return опциональным.
Короткие функции выглядят гораздо приятнее. return в 1-2-строчных функциях меня очень раздражает. А в C++ до недавнего времени константные функции могли быть только однострочными.
Кроме того, в Rust принято использовать return для нештатного завершения, обычно при ошибке. Так как исключения не поддерживаются, такая двойственность синтаксиса делает код более читабельным.
+3
А как всё же реализовать несколько точек выхода?
Точно так же: http://is.gd/WQfIrK
Суть в том, что можно опускать return в конце любого scope, получая значение последнего выражения в блоке: http://is.gd/UhSmzV
+1
В D на самом деле аналогично.
Да, знаю, просто не решился переделывать оригинальные примеры кода. Признаю, сравнение из-за этого может выглядеть не совсем объективно…
Хотя некоторая разница всё-таки есть: в расте невозможно дать имя модулю изнутри, так что аналога конструкции
module main;
нет.В D просто нет такого понятия как «неинициированная переменная», так как с ними много проблем.
Хм… а вот это что такое?
int a = void;
writeln(a); // ???
Вот, кстати, что это, чтоcast(long) a
— одинаково не удобны в выражениях, в отличие отa.to!long
Почему?
0
a as i64
Вот, кстати, что это, чтоcast(long) a
— одинаково не удобны в выражениях, в отличие отa.to!long
Они и должны быть громоздкими и обращающими на себя внимание, как по мне. Это ж преобразования типов — в этом месте легко вносятся логические ошибки.
+1
Да, и у вас заголовок статьи не компилируется :-)
0
Пойдет?
https://play.rust-lang.org/?gist=1df33e28a746408d0905c9c71a74a5fe
struct Man {
basics: &'static str,
}
macro_rules! man {
( $x:expr => $y:expr ) => {
Man{basics: "https://habrahabr.ru/post/280642"}
}
}
fn main() {
println!("{}", man!(D => Rust).basics);
}
https://play.rust-lang.org/?gist=1df33e28a746408d0905c9c71a74a5fe
+8
Зачёт! :-)
-1
Вот даже 2.0, а то чего-то я параметры макроса не использовал:
play.rust-lang
Эх, вечно я над всякой ерундой залипаю.
use std::collections::HashMap;
#[derive(PartialEq, Eq, Hash)]
enum Lang {
Go,
D,
Rust,
}
struct Info {
basics: &'static str,
concurrency: &'static str,
}
macro_rules! man {
($lang1:ident => $lang2:ident) => {{
let mut db = HashMap::new();
db.insert((Lang::Go, Lang::D), Info {
basics: "https://habrahabr.ru/post/279657",
concurrency: "https://habrahabr.ru/post/280378",
});
db.insert((Lang::D, Lang::Rust), Info {
basics: "https://habrahabr.ru/post/280642",
concurrency: "TODO: DarkEld3r еще не написал",
});
db.remove(&(Lang::$lang1, Lang::$lang2)).unwrap()
}}
}
fn main() {
println!("Go => D basics: {}", man!(Go => D).basics);
println!("Go => D concurrency: {}", man!(Go => D).concurrency);
println!("D => Rust basics: {}", man!(D => Rust).basics);
println!("D => Rust concurrency: {}", man!(D => Rust).concurrency);
}
play.rust-lang
Эх, вечно я над всякой ерундой залипаю.
+3
У Вас каждый man! новый хэшмап создает и заполняет каждый раз.
enum Lang {
Go,
D,
Rust,
}
use Lang::*;
struct Info {
basics: &'static str,
concurrency: &'static str,
}
macro_rules! man {
($lang1:ident => $lang2:ident) => {{
match ($lang1, $lang2) {
(Go, D) => Info {
basics: «habrahabr.ru/post/279657»,
concurrency: «habrahabr.ru/post/280378»,
},
(D, Rust) => Info {
basics: «habrahabr.ru/post/280642»,
concurrency: «TODO: DarkEld3r еще не написал»,
},
_ => unreachable!()
}
}}
}
fn main() {
println!(«Go => D basics: {}», man!(Go => D).basics);
println!(«Go => D concurrency: {}», man!(Go => D).concurrency);
println!(«D => Rust basics: {}», man!(D => Rust).basics);
println!(«D => Rust concurrency: {}», man!(D => Rust).concurrency);
}
enum Lang {
Go,
D,
Rust,
}
use Lang::*;
struct Info {
basics: &'static str,
concurrency: &'static str,
}
macro_rules! man {
($lang1:ident => $lang2:ident) => {{
match ($lang1, $lang2) {
(Go, D) => Info {
basics: «habrahabr.ru/post/279657»,
concurrency: «habrahabr.ru/post/280378»,
},
(D, Rust) => Info {
basics: «habrahabr.ru/post/280642»,
concurrency: «TODO: DarkEld3r еще не написал»,
},
_ => unreachable!()
}
}}
}
fn main() {
println!(«Go => D basics: {}», man!(Go => D).basics);
println!(«Go => D concurrency: {}», man!(Go => D).concurrency);
println!(«D => Rust basics: {}», man!(D => Rust).basics);
println!(«D => Rust concurrency: {}», man!(D => Rust).concurrency);
}
0
Ага, я об этом подумал уже когда запостил и даже накидал аналогичную версию 3.0, но потом решил что не очень клево превращать комментарии к статье в git)
0
Аналог на D:
import std.traits;
alias Lang = int;
enum : int {
Go ,
D ,
Rust ,
};
struct Info {
string basics;
string concurrency;
}
enum articles = [
[ Go , D ] : Info( "https://habrahabr.ru/post/279657" , "https://habrahabr.ru/post/280378" ) ,
[ D , Rust ] : Info( "https://habrahabr.ru/post/280642" , "TODO: DarkEld3r еще не написал, скорее бы" ) ,
];
template man( Lang function( Lang ) axis )
{
static if( is( FunctionTypeOf!axis args == __parameters ) )
{
enum from = mixin( __traits( identifier , args ) );
enum to = axis( from );
enum man = articles[[ from , to ]];
}
}
unittest {
static assert( man!(Go => D).basics == "https://habrahabr.ru/post/279657" );
static assert( man!(Go => D).concurrency == "https://habrahabr.ru/post/280378" );
static assert( man!(D => Rust).basics == "https://habrahabr.ru/post/280642" );
static assert( man!(D => Rust).concurrency == "TODO: DarkEld3r еще не написал, скорее бы" );
static assert( !is( man!(Go => Rust).concurrency ) );
}
void main( ) { }
+2
Скоро еще будет сравнение D с Pony ;)
+2
НЛО прилетело и опубликовало эту надпись здесь
Мелкие неточности из сравнения типов D и Go просочились сюда, например в Go byte это алиас для uint8, а rune для int32.
Согласен, но решил не переделывать оригинал. Меня ещё и int смущает, который имеет фиксированный размер в D, но зачем-то приведён как аналог платформозависимого int из Go.
Кстати, в Go есть возможность помечать функции как никогда не возвращающие управление? Насколько я вижу, в D были предложения ввести
@noreturn
атрибут. В Rust такие функции имеются и "результат" можно присвоить любому типу. Применяется, например, так: let a: String = match 10 {
10 => "normal value".to_owned(),
_ => panic!(),
};
0
Потому, что если вы пишете кроссплатформенный софт, то вынуждены писать его предполагая, что размер инта минимальный из возможных.
0
Дык, если нам надо, то будем использовать типы фиксированного размера. В Go int — это аналог ptrdiff_t, а не int из D.
0
Наоборот, аналогом ptrdiff_t в го является int. Но сам int имеет более широкую область применения, чем смещение указателей. Согласитесь, при портировании программы будет странно менять общий тип int на зачастую не относящийся к делу ptrdiff_t.
0
Наоборот, аналогом ptrdiff_t в го является int.
А в чём разница?
Собственно, я могу согласиться с тем, что название int для платформозависимого типа не самое удачное. В этом плане мне нравится решение раста:
isize/usize
длиннее типов с фиксированным размером, да и название говорящее.Но сам int имеет более широкую область применения, чем смещение указателей.
В Go или вообще? И неужели в D не возникает желания использовать "знаковый size_t" не только для разницы указателей?
А при портировании всё равно какие-то изменения вносить придётся, иначе код получится убогим. Заодно будет поводом пересмотреть действительно ли там необходим ptrdiff_t.
0
Разница в направлении портирования. В D больше типов, в Go меньше. Так что в одном направлении приходится сводить несколько типов к одному (что относительно просто), а в другом — выбирать нужный тип (и тут конечно есть сложности, ибо в коде не хватает информации для выбора правильного типа — нужно анализировать алгоритм).
И в Го и вообще. А почему должно быть желание использовать типы не по назначению?
И в Го и вообще. А почему должно быть желание использовать типы не по назначению?
0
НЛО прилетело и опубликовало эту надпись здесь
Да, panic — это макрос, который раскрывается в вызов как раз такой специальной функции:
Для наглядности мой пример можно и переписать заменив панику на собственную функцию:
fn begin_unwind<M: Any + Send>(msg: M, file_line: &(&'static str, u32)) -> ! { ... }
Для наглядности мой пример можно и переписать заменив панику на собственную функцию:
fn my_panic() -> ! {
loop {}
}
fn fake_panic() {}
fn main() {
let _a: String = match 10 {
10 => "normal value".to_owned(),
//_ => fake_panic(), // Error.
_ => my_panic(),
};
}
+3
Да нет, всё правильно. Не важно чему оно там алиас. Область применения у них эквивалентна.
0
Самое забавное тут то, что после изучения Rust отношение к D несколько изменилось — в плане лаконичности и выразительности последний сильно выигрывает. Впрочем, «явность» Rust-сообщество наоборот считает преимуществом. По моим ощущениям, в Rust чаще руководствуются «академической правильностью», а в D более практичный подход.
У меня после продолжительных экспериментов с Rust осталось очень похожее впечатление. Его строгость очень привлекает в больших проектах с критической важностью корректности, но затраты времени на написание простых вещей очень уж непрактичны. Вероятнее всего, даже если я решу начинать какой-либо новый проект на Rust, прототип всё равно будет написан на D.
0
но затраты времени на написание простых вещей очень уж непрактичны
Всё-таки думаю, что ситуация не настолько плоха. Если мы пишем что-то действительно простое, то и мест, где придётся "воевать" с бороу чекером будет мало. Сильнее всего это будет проявляться, если кода в общем мало и при этом активно изобретаются какие-то свои структуры данных — вот тут придётся вникать в тонкости языка и использовать unsafe. Если же пользоваться готовым, то всё куда проще. Конечно, у D есть сборка мусора, что должно давать выигрыш для быстрого прототипирования… хотя тут любители динамическиx языков предпочтут что-нибудь другое.
Впрочем, у меня маловато практического опыта с Rust (а с D вообще нет), чтобы делать окончательные выводы.
+2
Зарегистрируйтесь на Хабре, чтобы оставить комментарий
man!(D => Rust).basics