Яку сферу займе мова програмування Rust?
Що найбільше ЗАРАЗ пишуть на Rust’i, яку нішу він займе у майбутньому? Чи є в нього перспективи у Вебі? Які перспективи в нього в Україні? Та чи варто його вчити новачкам?
Що найбільше ЗАРАЗ пишуть на Rust’i, яку нішу він займе у майбутньому? Чи є в нього перспективи у Вебі? Які перспективи в нього в Україні? Та чи варто його вчити новачкам?
Никакую.
С сделали для написания Unix, C++ взлетел вместе с многооконным интерфейсом, Go с Докером и Кубером. А Rust не решает бизнес-задачу, он для демонстрации borrow cheker..
З того що я бачив влітку 2024, блокчейни та усілялі кріптовалюти.
Раст майже попер звідти с++.
Як на мене вчити варто, тому що там багато цікавих ідей. Тому в принципі мені незрозуміло, коли рекомендують обмежитися однією мовою, це звужує кругозір.
Власне цікава, але дуже цікава ідея там одна, до якої йшли від Едгара Дейкстери що додав в Algol вказівники, це компілятор сам управляє часом життя обьєктів в HIP дивлячись на скрізь по коду, де обєкт спочів. В дійстності теж є сусттєве обмеження — це коли Rust врапить С-шний API, а це наприклад будь який системний API. А так ML подібниц синтаксис і як колись писав Герб Саттер «Аналогія цих обмежень — давайте до усіх велосипедів включно з гоночними, прикручувати два граничних колеса с заду, томущо діти часто падають коли вчаться їздити».
Усе інше як то Cargo і т.д. це вже запозичення від інших мов.
Ще ADI там досить оригінальний та дозволяє писати безопасний багатопотоковий код.
Коментар порушує правила спільноти і видалений модераторами.
Коментар порушує правила спільноти і видалений модераторами.
Новачкам не варто вчити, складно і мало вакансій.
Сподіваюсь, що стане мовою загального призначення, без нішовості.
Поки що все що я бачив на расті — то все є різноманітні команд лайн тулзи (rigrep наприклад) — зазвичай дуже круті тулзи, набагато кращі та швидші аніж ті що встановлені за замоченням. Може це тому що круті деви хочуть спробувати раст (де як не в пет проєкті?), а може це і буде нишою, хз
Тобто якщо б я створював якийсь аналог grep — я б мабуть теж вибрав раст — потужна мова, компіляція у бінарь, швидка, безпечна (як мови із GC). Але якщо щось більш складне — навряд — воно буде боротьбою із борроу чекером у такому випадку — краще хай буде не таким швидким але без болю
боротьба з бороу чекером до пори поки ти не зламаєш звички набуті з С/С++
А навіщо їх ламати? Я працюю зі двозв’язними списками у Сі, знаю алгоритми, які їх використовують. Чому я маю від цього відмовлятися?
Це типу натяк, що у Rust не можна зробити двозвʼязний список?
Без unsafe у сішному стилі, та ще й з хаками на кшталт того, що є у танціючих зв’язках — ні. У стилі STL з C++ — так можна. Або замінити вказівники індексами :-)
Це втрата переваг, що надає Rust. Якщо у мене весь код unsafe, то... у чому різниця?
1. Чому це? У Rust багато переваг, не лише безпека памʼяті
2. Чого це «увесь код ансейф», якщо мова йшла лише про two-way linked list, та ще й у сішному стилі?
може використати який інший контейнер, або якщо треба бібліотеку для роботи зі списками, а не покладатися на сішні чи приплюснуті звички заради дрочу приросту перфоманса напівшишки?
Це більше C, а на C++. С++ так, завдяки STL це контейнерно-орієнтоване мислення. Але якщо брати двопов’язані циклічні списки, то кожен елемент може розглядатися як контейнер.
Знову ж таки, якщо ти прочитав усього Дональда Кнута, то відкидати усі ці знання лише тому, що його алгоритми не дуже лягають на контейнери? Або ламати голову, як їх перевести під контейнери з однієї точкою володіння? Як на мене тут роботи більше, ніж ловити проблеми, пов’язані з гарантіями, що надає Rust.
кнута не читав, но осуждаю
Як на мене тут роботи більше, ніж ловити проблеми, пов’язані з гарантіями, що надає Rust.
які проблеми надає руст із гарантіями?
ми про то й самий руст, чи просто хтось його неосилятор?
1. Чому це? У Rust багато переваг, не лише безпека памʼяті
Так, алгебраїчна теорія типів. Але коли ми пишемо крізь unsafe
то якось напружує.
Чого це «увесь код ансейф», якщо мова йшла лише про two-way linked list
Так вони скрізь :-) Скажімо так, я не бачив гарного рішення, усі мої намагання зайти у Rust для свого кола задач розбилися про те, що я не зміг нормально працювати зі списками.
Але ж ми не пишемо крізь unsafe. Я писав багато на Rust, і використовував unsafe декілька разів усього.
Скажімо так, я не бачив гарного рішення, усі мої намагання зайти у Rust для свого кола задач розбилися про те, що я не зміг нормально працювати зі списками.
Можна подробиці? Чому не можна зробити безпечну обгортку над списком, а всередені мати unsafe?
`unsafe` багато використовується у взаємодії з небезпечним оточуванням (інтероп, ембед, доступ до хардвера) — але це природньо.
Можна подробиці? Чому не можна зробити безпечну обгортку над списком, а всередені мати unsafe?
Ну... якщо у тебе в алгоритмі 90% операцій це списки, то який сенс виділяти десь unsafe? Взагалі, можливо це й можливо, але я не зміг. Взагалі, коли у тебе якось графова мережа, я купою посилань між елементами, зі своїми пулами алокацій, то... ти починаєш це імлементувати ці обгортки, читати про різні маркери життя змінних, а потім... Нехай йому Грець, зроблю на день на Сі та забуду, ніж витрачати тижні на ці обгортки.
Якщо пан має час та натхнення, то може спробувати реалізувати dancing link я тому вигляді, як він описаний у Д. Кнута, наприклад,
arxiv.org/pdf/cs/0011047.pdf
або тут
www.inf.ufrgs.br/...7.2.2.1-dancing_links.pdf
Я розумію, що краще починати вивчати Rust не з таких задач, але мені подобаються саме такі.
інтероп, ембед, доступ до хардвера
Це як раз зрозуміло, тут проблем немає.
15 років в ембедеді, кнута не читав, списки вертів на одному місці (бо динамічне виділення пам"яті).
в с++ береш вектор і на 99% випадків вистачає, решта 0.99% мапа, а список це для випускників Гардварда і літкоддрочерів
в руст не можна робити список без унсейф -> ваш руст херня, амінь
А чому динамічне виділення пам’яті? Це не обов’язково, якісь елементи можуть бути й статичними, списку пофік на це. Не кажучи про те, що ми можемо бути обмежені кількістю пам’яті, тому нам треба бути самому вирішувати, що використовується не часто й може бути звільнено, а що ми готові перерахувати. Наприклад, користувач виділив для програми 64G пам’яті, а у середині ми самі робимо виділення, та звільнення.
Також можна виділяти великими шматками відразу по скільки не шкода́, а у середині швидкий fixed size allocatior, ... Це просто інший трохи світ, інші паттерни, ...
усі мої намагання зайти у Rust для свого кола задач розбилися про те, що я не зміг нормально працювати зі списками.
тому що не треба із сішним уставом не ходять до рустового монастиря
Я і не ходжу. Якщо у мене буде networking з багатьма потоками, з якимось спільним станом, який треба оновлювати, я буду дивитися на Rust. А от якщо у мене буде якийсь бектрекінг, то я скоріше за усе оберу то, де у мене більше досвіду.
unsafe у Rust не вимикає перевірки borrow checker-a та не перетворює Rust на C чи Assembler. unsafe дозволяє трохи розширити можливості Rust але із умовою, що програміст має розуміти, що він робить и що це не приводить к помилкам чи UB. Тобто використати інваріанти, які система типів Rust не може перевірити, бо для цього в не недостатньо інформації. Якщо суто технічно, то unsafe лише дозволяє робити наступні речі:
— зробити розіменування сирого вказівника (dereference of raw pointer)
— виклик небезпечної функції чи методу (Call an unsafe function or method)
— доступ чи модифікація модифікуємої статичної змінної (Access or modify a mutable static variable)
— реалізація unsafe трейту (Implement an unsafe trait)
— доступ до полів у об’єднанні (Access fields of a union)
Proof link: doc.rust-lang.org/...t.html#unsafe-superpowers
Усі перевірки та система типів працює як і раніше! This common misperseption of unsafe!
А чи дозволяє unsafe перетворити вказівник на адресу, зробити певні маніпуляції з ним, а потім привести до іншого типу? Наприклад
struct dlist { struct dlist * next; struct dlist * prev; }; struct some_type { int data; struct dlist link1; struct dlist link2; } struct some_type * link1_to_some_type(struct dlist * link1) { intptr_t addr = (intptr_t) link1; addr -= offsetof(struct some_type, link1); return (void*) addr; }
В принципі, я впевнений, що Rust це зробити дозволить. На свій страх ти можеш робити усе, що заманеться. Тут проблема у тому, що це буде дуже сильно конфліктувати з автоматичним менеджментом пам’яті, що перетворить розробку на пекло, бо тобі треба буде усе все що під капотом прокручувати в мозку. Бо з точки розу Rust змінна не потрібна, що на неї немає референсів. Але я можу її адресу отримати аріфметично та використати.
Може ще до смаку тобі прийдеться! ;)
rust-unofficial.github.io/too-many-lists
Як раз для полюблювачів зв’язних списків! ;)
То не зв’язні списки, то контейнер, який використовує зв’язні списки для зберігання елементів. Десь там знайшов такий код:
fn walk_aboot() { let mut list = List::new(); // [_] list.push_left(0); // [0,_] list.push_right(1); // [0, _, 1]
Так от, let mut list = List::new();
це вже маячня, бо двозв’язні списки це не обов’язково контейнер, чого контейрно-орієнтовані розробники (Rust, C++) не розуміють.
Взагалі, тест доволі простий: цікавить ефективна реалізація алгоритму dancing link с посиланнями замість індексів. Також це гарний приклад того, як використовуються такі списки, щоб зрозуміти їх сутність. Бо це вже не перший раз, коли Rust програмісти намагаються написати двозв’язні списки, не розуміючи, що треба зробити. Або C++ програмісти намагаються довести, що двозв’язні списки є в STL, ...
а для чого? яку проблему вирішує зв«язаний список? чи можна обійтися іншим? чи дійсно потрібно мати контейнер «на купі», з динамічним розміром? а чи можна зробити інший дизайн і не морочитися із зв"язаним списком?
Ок,
1. У порівнянні з контейнерами, щоб видалитися з нього нам навіть не треба знати, в якому ми контейнері. Наприклад, є список активних задач, ж список задав у черзі, а є список задач в очікуванні. Коли задача закінчується, їй взагалі не потрібно знати, в якому зі списків вона є. Достатньо
struct dlist * prev = me->prev; struct dlist * next = me->next; prev->next = next; next->prev = prev;
2. Кожен елемент сам може бути контейнером
Ми можемо ітеруватися по списку, навіть не знаючи, до якого контейнера він належить. Більше того, якщо у нас є 5 елементів, і вони ініціалізовані як окремі вузли, то кожен з них можна розглядати як самостійний контейнер, що корисно в кластеризації
A B C D E
Якщо ми зв’яжемо B перед C, ми отримаємо вже чотири незалежні контейнери:
A B C D E
3. Якщо ми видалили елемент, ми можемо швидко повернути його назад, що особливо корисно для реалізації бектрейсингу:
/* Видалення */ struct dlist *prev = me->prev; struct dlist *next = me->next; prev->next = next; next->prev = prev; /* Пошук в глибину */ /* Відкат */ me->prev->next = me; me->next->prev = me;
Це властивість використовується в алгоримтмі DLX, без неї реалізація неефективна: Dancing Links, Donald E. Knuth, Stanford University
Сюди з різні пути та менеджери пам’яті, коли ми можемо одночасно виділити блок елементів, зв’язати його та потім брати по одинці елементи з нього. Типовий код виглядає приблизно так:
#define N 10 struct dlist { struct dlist *prev, *next; }; struct element { int data[42]; }; struct unused_element { struct dlist link; }; static struct dlist free_list = { &free_list, &free_list }; void dlist_insert(struct dlist *head, struct dlist *node) { node->next = head->next; node->prev = head; head->next->prev = node; head->next = node; } void dlist_remove(struct dlist *node) { node->prev->next = node->next; node->next->prev = node->prev; } #define container_of(ptr, type, member) \ ((type *)((char *)(ptr) - offsetof(type, member))) void initialize(void) { struct element *buf = malloc(N * sizeof(struct element)); if (!buf) { perror("malloc failed"); exit(1); } for (int i = 0; i < N; ++i) { struct unused_element *unused = (void *)(buf + i); dlist_insert(&free_list, &unused->link); } } struct element *alloc_elem(void) { if (free_list.next == &free_list) { return NULL; } struct dlist *link = free_list.next; dlist_remove(link); return (void*)container_of(link, struct unused_element, link); } void free_elem(struct element *elem) { struct unused_element *unused = (void *)elem; dlist_insert(&free_list, &unused->link); }
Питання було у тому, які проблеми вирішують циклічні двозв’язні списки. Я відповів з прикладами. На Rust це буде unsafe і не так виразно.
Сюди з різні пути та менеджери пам’яті, коли ми можемо одночасно виділити блок елементів, зв’язати його та потім брати по одинці елементи з нього. Типовий код виглядає приблизно так:
🥱 Вот на Rust с интерфейсом аллокатора, что позволяет использовать этот аллокатор со смарт поинтером в безопасном коде:
play.rust-lang.org/...58fba45418e8bfd240a047948
Плюс еще github.com/...dev/linked-list-allocator или github.com/...os/buddy_system_allocator, имеющих внутри связный список или более сложную структуру.
Звісно, що можна перекласти, якщо використовувати unsafe. Взагалі є думка, що написати транслятор з Сі у unsafe Rust суто технічне питання. І так можно перевести увесь Linux. Але навіщо?
Ну а так в коді, як на мене, забагато машинерії: cast, unwrap... Тобто тобі треба слідкувати за купую деталів.
Звісно, що можна перекласти, якщо використовувати unsafe. Але навіщо?
Это то, что люди, которые не пишут на Rust, плохо понимают (но почему-то считают за истину).
Поинт не в том чтоб полностью избавиться от всего unsafe кода. Он в расте постоянно используется, особенно в инфраструктурных частях кода.
Поинт в том, чтоб изолировать его в нескольких частях системы или даже в отдельных библиотеках, чтоб остальная часть кода не требовала использования unsafe.
Весь поинт примера, который я привел, не там, где идут raw pointers, а в последней функции main:
struct Something { data: [i32; 42], } fn main() { let capacity = 100; let allocator = LinkedListAllocator::<Something>::new(capacity); let mut elements = vec![]; for _ in 0..(capacity - 1) { elements.push(Box::new_in(Something { data: [42; 42] }, &allocator)); } assert!(Box::try_new_in(Something { data: [42; 42] }, &allocator).is_err()); drop(elements); let mut elements = vec![]; for _ in 0..(capacity - 1) { elements.push(Box::new_in(Something { data: [42; 42] }, &allocator)); } assert!(Box::try_new_in(Something { data: [42; 42] }, &allocator).is_err()); }
0 unsafe блоков, смарт поинтеры, работающие поверх аллокатара, пример которого вы выше привели, автоматическое управление памятью, отсутствие dangling pointers, use after free, double free, memory leak, и т.п..
В качестве бонусов: можно создать несколько аллокаторов, которые работают с разными типами (привет дженерикам C, вернее их отсутствию):
struct SomethingElse { a: u32, b: u32, } fn main() { let capacity = 100; let allocator1 = LinkedListAllocator::<Something>::new(capacity); let element1 = Box::new_in(Something { data: [42; 42] }, &allocator1); println!("{:?}", element1.data); let allocator2 = LinkedListAllocator::<SomethingElse>::new(capacity); let element2 = Box::new_in(SomethingElse { a: 42, b: 24}, &allocator2); println!("{} {}", element2.a, element2.b); }
И в качестве вишенки на торте, borrow checker отслеживает чтоб смарт поинтер не использовался после того как сам аллокарор деаллоцирован, вот этот код не скомпилируется:
fn main() { let capacity = 100; let allocator1 = LinkedListAllocator::<Something>::new(capacity); let element1 = Box::new_in(Something { data: [42; 42] }, &allocator1); drop(allocator1); println!("{:?}", element1.data); }
так же как и этот:
fn create_something_out_of_nothing<'a>() -> Box<Something, &'a LinkedListAllocator<Something>> { let capacity = 100; let allocator = LinkedListAllocator::<Something>::new(capacity); return Box::new_in(Something { data: [42; 42] }, &allocator); }
Ну а так в коді, як на мене, забагато машинерії
Человеку, который не знает Rust и не пишет на нем, кажется что в Раст коде что-то не так. Ок, держите нас в курсе (нет).
cast
struct unused_element *unused = (void *)elem;Неявный каст void * к другому типу поинтера это варнинг в последних версиях GCC.
unwrap
В коде реально 3 unwrap, два из которых там абсолютно должны быть — они проверяют чтоб при вычислении размера блока для аллокации не произошло тихое переполнение целого числа, ведущее к уязвимости выхода за пределы буфера.
Тобто тобі треба слідкувати за купую деталів.
Это аллокатор, в нем по определению нужно следить за деталями.
Например, в C реализации нужно следить за тем, чтоб эти 2 типа имели одинаковые выравнивание:
struct dlist { struct dlist *prev, *next; }; struct element { int data[42]; };
Если изменить data[42] на data[43], то код начинает триггерить неопределенное поведение из-за операций с невыровнеными указателями.
Или чтоб размер element был больше размера dlist. Если сделать int data[2], то закончится очень плохо.
Я уже не говорю про использование функций alloc/free, которые при неправильном использовании прямо ведут к уязвимостям и неопределенному поведению (проблемы, которые невозможно воспроизвести с смарт поинтерами в моем Rust коде).
Неявный каст void * к другому типу поинтера это варнинг в последних версиях GCC.
g++ так, gcc — ні.
Поинт в том, чтоб изолировать его в нескольких частях системы или даже в отдельных библиотеках, чтоб остальная часть кода не требовала использования unsafe.
Я це прекрасно розумію. Особисти у мене там де треба швидкодія, то виникає крізь unsafe, і при програмуванні на Rust ти більше воюєш з мовою, чим розв’язуєш задашу. Думаєш про артітектуру, як зробити більше safe, ... В рузультаті розробка йде дуже повільно. Коли я пишу на Сі, то помилок з пам’ятью майже немає, вони знаходяться відразу, а от в лозіці є.
А коли швидкодія не потрібна, то Rust-у до виразності Haskell як до Місяця.
В качестве бонусов: можно создать несколько аллокаторов, которые работают с разными типами (привет дженерикам C, вернее их отсутствию):
Ще раз, я написав код більше як демо використання двозв’язних циклічних списків. Це лише один з можливих прикладів, імплементація на Rust також їх використовує, тому ок. А тиак зазвичай алокатор приймає розмір одного елемента, та кількість, та просто повертає void * а ти його вже кастиш до того, що тобі треба. Можливо пару функцій init/done для ініціалізації певних полів, яка буде зберігатися між alloc/free.
И в качестве вишенки на торте, borrow checker отслеживает чтоб смарт поинтер не использовался после того как сам аллокарор деаллоцирован, вот этот код не скомпилируется:
Я це прекрасно розумію... Просто,припустимо, що алокатор це з’єднання. Ініціаліується один раз при старті, деініціалізується один раз останнім кроком перед завершенням. Шансів, що ти наступиш на граблі використання після деалокації трохи менше нуля.
Так, можна сказата, що в разі Rust треба подумати про це треба лише один раз. Але... наприклад, ми хочемо виділити усі конекшени разом, але мати не один спільний контейнер, а окремий під кожний робочій потік. І вже в нас виникає кастомна реалізація.
fn main() { let max_worker_connections = 100; let worker_count = 24; let allocator = LinkedListAllocator::<Something>::new(max_worker_connections, worker_count); let current_worker = 7; let element1 = Box::new_in(Something { data: [42; 42] }, &allocator1, current_worker);
Если изменить data[42] на data[43], то код начинает триггерить неопределенное поведение из-за операций с невыровнеными указателями.
Не буде. malloc(N * sizeof(struct element))
це стандатна сішна практика, компілятор сам вирівняє розмір структури, якщо потрібно. Так, як ви зауважили, якщо змініти int data[42]
на char data[5]
, тоді пробеми, бо має бути sizeof(struct element) >= sizeof(unused element)
. Аде в Сі також є union, тільки шанс наступити на ці граблі трохи менше нуля, а там набагато виразніше. int data[43]
це добре, але кеш не буде жаліти, і трохи TLB.
А так зазвичай на проді трохи більше магії, по-перше, виділяєш через memalign
, по-друге, самостійно вирівнюєш розмір елемента, щоб лагідніше з кешем працювало:
if (element_size < sizeof(struct unused_element)) { element_size = sizeof(struct unused_element); } int granularity = 128; int mod = element_size % granularity; if (mod != 0) { element_size += granularity - mod; } void * buf = memalign(granularity, element_size);
Я уже не говорю про использование функций alloc/free, которые при неправильном использовании прямо ведут к уязвимостям и неопределенному поведению
Тобто std::alloc::alloc
не призведе?
g++ так, gcc — ні.
На godbolt последние версии gcc и clang показывают варнинги, разбирайтесь.
при програмуванні на Rust ти більше воюєш з мовою, чим розв’язуєш задашу. Думаєш про артітектуру, як зробити більше safe, ... В рузультаті розробка йде дуже повільно.
Проблема отсутствия опыта. Пишите больше на Rust и меньше на C, проблема пропадет сама собой.
А тиак зазвичай алокатор приймає розмір одного елемента, та кількість, та просто повертає void * а ти його вже кастиш до того, що тобі треба.
Это ж офигенно, постоянно следить за тем, чтоб случайно не напутал в какой аллокатор как размер передал и к какому типу его ОК кастить, а к какому нет. Я в курсе, что так «обычно делают в С», но есть более интересная альтернатива: аллокатор, который принимает дженерик и на его основе сам посчитывает размар типа данных и выдает тебе типизированый указатель смарт поинтер.
И, кстати, размер типа недостаточно, нужен размер + выравнивание, про что очень часто забывают. На всякий случай, просто иметь поинтер, который неправильно выравнен в С — это уже неопределенное поведение (даже если вы не делаете дереференс): port70.net/.../c11/n1570.html#6.3.2.3p7
Не буде. malloc(N * sizeof(struct element)) це стандатна сішна практика, компілятор сам вирівняє розмір структури, якщо потрібно.
malloc даст указатель, который будет выровнен на 8 байт (достаточно для struct element и struct dlist). Проблема в том, что дальше вы делаете арифметику с указателями, которая это выравнивание собьет.
for (int i = 0; i < N; ++i) { struct unused_element *unused = (void *)(buf + i); dlist_insert(&free_list, &unused->link); }
Размер int data[43] это 172 байта, выравнивание dlist 8 байт. 172 не делится на 8, значит эта строчка делает каст поинтеров и выдает поинтер, который потенциально не выравнен (более точно, в данном случае каждый второ поинтер не будет выровнен корректно), добро пожаловать в мир неопределенного поведения.
Аде в Сі також є union, тільки шанс наступити на ці граблі трохи менше нуля
Только юнион в вашем коде почему-то не используется, и мне надо лазить по всему коду и искать косяки.
Тобто std::alloc::alloc не призведе?
Странно, выше писали, что:
Поинт не в том чтоб полностью избавиться от всего unsafe кода. Он в расте постоянно используется, особенно в инфраструктурных частях кода.Поинт в том, чтоб изолировать его в нескольких частях системы или даже в отдельных библиотеках, чтоб остальная часть кода не требовала использования unsafe.
Я це прекрасно розумію.
И опять приводите вопрос, который показывает, что вы этого не понимаете.
На godbolt последние версии gcc и clang показывают варнинги, разбирайтесь.
С++ звісно показує warning, Якщо вибрати С, то ніяких попереджень немає: link
Проблема отсутствия опыта. Пишите больше на Rust и меньше на C, проблема пропадет сама собой.
Код, що я привів, це п’ять хвилин. А скільки це займе часу на Rust? Так, зараз є ChatGPT, що теоретично можна записати. Але до цього особисто я витрачав цілий день на пошук (1) які є альтернативи у safe; (2) як не зробити самому unsafe; (3) вивчення документації, які можливості є; (4) за довгий час виникає потрібна реалізація; (5) треба внести зміни й знову не видно як це робити. Тому писати в сішному стилі на Rust у мене не виходило ніяк, то я закинув. Але в стилі С++ ніби-то норм.
Тому, якщо тобі платять погодинно, то чому б і не Rust. А для себе... Ти хочеш написати щось цікаве, а не розбиратися в тому, як написати щось цікаве. Життя занадто коротке.
Только юнион в вашем коде почему-то не используется, и мне надо лазить по всему коду и искать косяки.
Ще раз, (1) я не ставлю мету написати 100% safe, для мене приорітет виразність; (2) код написаний за 5 хвилин без перевірок, у вікні редагування dou, чисто як демо використання. Я навіть його не компілював.
Якщо бізнес задача писати швидко код, то може нах той Rust, пишем далі швидко і потужно на С
Про це є дискусія в іншому топіку, де обговорюють, чи треба Rust в ядрі Linux. Я просто їх плутаю постійно, бо відповідаю на конкретний пост.
Ну... якщо брати grep
, то... У чому перевага у порівнянні з Сі, на якому вона написана? Це утиліта командного рядку, яка працює в один потік. Тому значна частина type safety не буде використана. memory leaks? По-перше, це в принципі досить гарно тестується. По-друге, це не дуже й критично, бо утиліта завершить виконання достатньо швидко. memory safity? Я не пригадую, щоб вона падала, працює стабільно, ...
Для мене Rust це все ж таки щось те, що працює довго, у багато потоків.
якщо брати grep, то... У чому перевага у порівнянні з Сі, на якому вона написана? Це утиліта командного рядку, яка працює в один потік.
Чому?
Можна декілька файлів паралельно перевіряти.
Можна один файл розбити на великі шматки і теж перевіряти їх паралельно. (Особливо коли номери рядків не потрібні. Але і з ними можна потім підкоректувати.)
-
Rust ще активний у wasm ніші. Тут є потенціал як на мене — і фронт, і фулстек, і serverless, і edge, і майже нема конкурентів
TypeScript як конкурент, але згода можливо тут Rust краще за багато що. Зазвичай просто під WASM не пишуть, а переносять існуючий C/C++ код, щоб робити WEB застосунки із десктопних. Та це поки що.
С# - все те саме + підтримка від MS + немає боротьби із борроу чекером. Не таке швидке, але начебто це нікого не *** - дев час дорожчий ніж сервери
тоді всі будуть бомжувати на вулиці, бо чат жпт всіх поглине
Потім він поглине сам себе, IT перестане існувати і повернемось до витоків. Будемо починати із Common LISP
Поки Rust — це блокчейн, крипта та cloud-рішення й нові БД.
На Rust-і написана Pingora, як заміна Nginx.
Я також хочу бачити Rust у розробці веб-серверів, але поки таких вакансій дуже мало.
В Україні є спільнота для вивчення й обговорення Rust-у t.me/rustlang_ua/2383 .
Поки Rust — це блокчейн, крипта
І то Golang звідти її потихеньку вижимає. Бо криптою починає цікавитись фінтехи/банки, а вони люблять Go. І Binance зараз активність саме у Golang напрямку показує (наприклад така шедевральна ліба github.com/bnb-chain/tss-lib), навіть Trust Wallet потихеньку врапери під Go пишуть.
Знайомий який давно у крипті каже що раніше багато чого на Rust писав, зараз більше Go
Чим популярнішою стає кріпта тим більш доступнішими мають бути технології. Тому думаю що Rust навіть звідти вижмуть.
Бо криптою починає цікавитись фінтехи/банки
Чета кажется невероятным, что банковские консерваторы предпочтут детское полупокерное го вместо джавы и оракла.
Я детально не вивчав прям ринок, наш CEO який у фінтехах/банках із штатів та Мексики крутиться так каже. Поки я у цій сфері пів року тому вірю на слово.
Году в 17 набирали команду, вполне себе на Scala, какой-то BigData занимались, мы правда с этой командой мало общались. Клиент был eCommerce/Fintech продавал товары в потребительский кредит — мы конечно, жостко Oracle стек. Сейчас все сильно поменялось, Oracle достал многие енпрайсы своей политикой особенно по лицензиям. Впрочем всеравно Java стек все ещё рулит, но уже больше в микросервисном исполнении. Может ли Rust проникнуть — да элементарно, решит кто то написать сервис и погнало.
фінтехи/банки, а вони люблять Go.
вони люблять древні технології, типу JAVA/C++ і тому подібне
Швидше перевірені. На модернових і Java і С++ писати нормально. Тут інша справа — в таких проектах куча гімнокодного легасі яке треба комусь супортити. Скажімо бізнесу дуже треба зробити якись напис рожевим, бо це «важливо для бізнесу». І для цього треба супер-дупер тім лід з 10+ роками досвіду, не меньше.
Не тільки супортити, а і розвивати. Напр. треба зробити інтеграцію з якою байдою. Ну не писати ж з нуля.
Ясно що, що треба, бо хто ще зможе розгрібати івно мамонта.
Хіпстери набіжать і скажуть фуфу, будем переписувати на гошці/русті/ноді, тільки хто їм дасть.
На Rust — огромная часть крипты. Каждый второй (или даже больше) от кого вообще слышу что реально пишет на Rust — в крипте.
Область специфическая, но как бустер для языка работает неплохо.
Про Firefox рядом сказали.
А так — сейчас он везде, но количества пока не очень заметные. Наблюдаем...
По суті крім FireFox особливо відомого нічого нема. Ще от в ядро Linux щось приймають написане на Rust. Ніша зрозуміла, це чітко зроблено як конкурент С++ в десктопних аплікаціях і системному рівні типа ML. Як порівнювати із modern C++ 11+, то особливих переваг нема, скоріше є недоліки. Як брати звідти ідею санітазінгу пам’яті на рівні компілятора — чудова ідея яка має потрапити і в С++ і в С також, принаймі ворнінги кидати. Ну а мови програмування яка захищає від не ефективного програмування, винайти не можливо — простіше навчити безпосередньо кодувати нейронну сітку. Взагалі ті хто створюють нові мови програмування, завжди пропонують бізнесу одну і ту саму ідею — ви зможете задіяти не підготовлених і відповідно дешевих людей у виробництві. Бізнес дуже цього прагне, бо як і усі хоче грошей, але так воно не працює і ніколи не працювало. Як працює — підвищення продуктивності на одну людину.
Проблема ваших висновків заключається в тому, що ви порівнюєте Rust з С/С++, в той же час він, скоріше за все, займе нішу джави.
Це навряд, тут більше у Go lang були би перспективи і D. За великим рахунком у Java та сама головна проблема, що і C++ — дуже багато коду створеного на попередніх версіях мови, а також дуже швидко випускаються усе нові версії мови які подекуди ламають зворотню сумісність, при чому в тих частинах де використовувались воркераунди десятками років і команда розробки нарешті взялась за вирішення давно відомої проблеми. Мови програмування губить і виводять на олімп інші речі, зазвичай принципові зміни бізнесу. Візьмемо скажімо Python, був собі нішевою скірптовою мовою програмування використовувалась десь для девопсії різної як тільки професори з Університетів не почали її використовувати для AI/ML, а це виявилось вагомим бізнесом. Тепер Python це вже мейнстрім. Те саме С++ знову на коні.
Дивись тему про TIOBE 2022. Про росповсюдженість стандарту, зараз це 14 та 17 для відносно нових проектів. 20 стандарт ще досі в повному обсязі не підтримується жодним із компіляторів. Більшість активного коду, а це вже мільярди строк підтягнули принаймі до 11 стандарту, що вже можна класифікувати саме як modern C++, позаяк 14 та 17 проміжні версії стандарту.
тут і зараз у вакансіях вимагають 14,17,20
неасілятори розробники компіляторів,
це ж треба так оконфузитися,
але с++ впєрдє планєти всєй,
не те що руст
тільки що роблять с++ в темі про руст?
доказують що?
зробіть тему с++ круче і доказуйте.
нагадує поведінку райдужно-кольорових латентних і не дуже
У вакансіях часто пишуть те, що вміють написати. Якщо придивитись то на справді — швидше не знання якихось особливостей додатків до стандартної бібліотеки типу index_sequence доступних із
в ідеальному світі рожевих поні можливо і так, а в реальному є в бодішопа вилка $3000-$4000 і треба заповнити ХХХ жопомісць і абракадабра у описі вакансії
А от дуже навряд запрограмуйте проектний контролер керування дроном, чи керування кавоваркою за такий самий час як це робила людина, що це вже неодноразово це робила скажімо на С.
нікому воно нах не потрібно, хіба фанатам залізячникам
Ну і так само людина, яка писала такі контролери на С, напише їх на Rust. Бо там складність не у мові
Як на мене, за рахунок алгебраїчної теорії типів, код Rust, хоча і поступається Haskell, але набагато стабільніший за Java тим, що на відміну від ООП більше перевірки різних станів, які можуть виникати та про які розробник не подумав. Але на Rust легше мігрувати імперативним розробникам. Тому там де треба більше гарантій, то Rust легко може бути заміною.
C# і Java — мають компілятори і GC. Ну ладно, ти скажеш, що там є віртуальна машина.
Але ж компілятори та GC мають Go, Ocaml, Haskell, Nim і ще купка інших мов — компілюються в платформо-залежний бінарник
Також, можна причепити GC навіть до C і C++ — en.wikipedia.org/...i/Boehm_garbage_collector
Звідси і виникло моє запитання.
у тебе талант прикидатись шлангом
процитую твій допис
тому що компільовані мови без GC
І навпаки — C# можна скомпілювати в нейтів бінарь, але ж так ніхто не робе — learn.microsoft.com/...ore/deploying/native-aot
Також, можна причепити GC навіть до C і C++
Важливе не те, коли «можна причепити», а коли не можна (крім суворо означених червоними флажками місць) обходити контроль доступу до памʼяти.
Тому C/C++ — мови без AMM (точніше так казати, а не про GC).
А навіщо GC у Haskell? У будь якій чисто функціональній мові ти не можеш створити circular references, тому простий підрахунок посилань працює добре, у більшості випадків це може зробити навіть компілятор.
Як порівнювати із modern C++ 11+, то особливих переваг нема, скоріше є недоліки.
лишайтеся на С++, не ходість в руст, там недоліки, переваг нема
Ви перекручюєте. Хто має бажання — хай пише на іржавому, не має хай не пише. Це джуніорскі заяви на рівні відкритих листів — «Припиніть використовувати C/C++», і відповідей голови комітету «Припиніть писати такі листи» і ще кучи виступів на конференціях де топові плюсовіки показують кейси і бенчмарки, в яких видно, що у цілому ряді моментів іржавий та голанг причмоктують. Хоче хтось — хай пише. От мій улюблений Firefox переписали частково — він став краще працювати.
Як порівнювати із modern C++ 11+, то особливих переваг нема, скоріше є недоліки.
це
джуніорскі заяви
Ну зробіть статті та бенчмарки де покажете, що іржавий на голову б’є модерновий С++, за якимись ключовими показниками. По суті це просто іньша мова, яка дає такий самий результат. Такої різниці як між ассемблером і Фортраном точно нема. Є одна єдина фішка — санітайзінг пам’яті на рівні компілятору, що швидше фішка компілятору. Мова така сама складна як і С++, і одночасно все ще недостатньо складна як і С++. Це усе священні війни. Колись було те саме Delphi проти С++ і я оскільки писав на Delphi був на стороні останньої. Кінцевому користувачеві глибоко пофігу на який мові написан софт, якість самого софта швидше залежить не від мови — а від кваліфікації програмістів. Не пофіг тільки самим програмістам, які використовують тех стек як міру конкуренції за місце під сонцем та просто похоліварити.
ну зробіть тему про с++ і доказуйте бенчмарки самі собі,
чого понабігти в тему руст і гадити плюсами?
чому у приплюснутих підгорає на руст?
топові плюсовіки показують кейси і бенчмарки, в яких видно, що у цілому ряді моментів іржавий та голанг причмоктують.
тільки не зрозуміло, чому підгорає,
написав мовою:
лишайтеся на С++, не ходість в руст, там недоліки, переваг нема
де топові плюсовіки показують кейси і бенчмарки, в яких видно, що у цілому ряді моментів іржавий та голанг причмоктують
Тобто, С++ обирають за перформанс? Чому тоді не С?
Чим принципово плюси можуть бути швидше? Ті самі алгоритми, записані іншим синтаксисом, і скомпільовані у LLVM, так само, як роблять і деякі плюсові компілятори. Збірки сміття немає, обовʼязкових віртуальних викликів теж немає. Такі ж самі zero-cost abstractions.
То ж звідки взятися принциповій різниці? Чи там різниця типу на 15%, і плюсери такі «ну все, ***ня, повільне лайно, пайтон»
Може плюсовики с конференцій порівнюють звичайний код на Rust з видроченим та оптимізованим кодом на плюсах?
Тобто, С++ обирають за перформанс? Чому тоді не С?
Чим принципово плюси можуть бути швидше? Ті самі алгоритми, записані іншим синтаксисом, і скомпільовані у LLVM, так само, як роблять і деякі плюсові компілятори.
Колись порівнював: сішний qsort суттєво — процентів 20 десь — повільніший за STL-ний sort.
Чому? Бо qsort використовує callback, а sort використовує перегружену операцію «<», яка доступна «всередині» шаблонної функції і яку навіть можна заінлайнити.
Тобто, різниця були чисто технічна і залежала тільки від реалізації мови і конкретного компілятора.
Але точних цифр не пам’ятаю, бо це було дуже давно, років 15 тому
Ну і я про це. У Раст немає обоʼвязкового рантайму, віртуальної машини, збірки сміття, у ньому зіро-кост абстракції, у ньому є макроси, у ньому можна дрочити байти і біти, можна робити асемблерні вставки.
І тут приходять плюсери, і розказують про якесь величезне відставання на порядки. От мені і цікаво, звідки воно в принципі може взятися (окрім оптимізацій компілятора)
Там є куча різниць, одна з головних це згода про визови які в расті зроблені вкрай паскудно. C/C++ для 64 біт зараз використовують fastcall де в 65% випадків усе обходиться регістрами Rust — завжди звертається до пам’яті, крім інлайнінгу. Іржавий та Го ніяким чином не захищають від не документованої поведінки, наприклад переповнення значення знакового числа, втрати точності чисел із плавоючою комою (навідміну наприклад від COBOL), не вірної роботи із системними визовами чи іншими С функціями, циклічною рекурсією і переповнення стеку і так далі. Звісно у С і С++ є недоліки, інакше ніхто би не писав нові мови зокрема і на заміну бо набридла бюрократія комітету. Велика ціна малої помилки, часто крах програми. Фікситься санітайзерами, статичним аналізом і тестами. Exceptions safety і самі exceptions — далеко від ідеалу. Атомарні операції — зроблено прямо кажучи костильно, через бібліотеку і не дуже так само як багато-поточність взагалі. Скудновата стандартна бібліотека, відповідно певний рівень прив’язки до фреймверків навіть у питанні базового функціоналу, наприклад роботи із UNICODE строками символів, або велосипедо-будування або DLL Hell. Код з одного компілятору потребує доопрацювання для іншого, «на халяву» зробити якусь крос платформіну і компілятор незалежну бібліотеку просто написавши код не вийде, або вона буде занадто не ефективна щоб її писати на С++. Мова погано підходить для швидкого протипування чи навіть скріптінгу, навідміну від скажімо Java,C# чи Python.
преконав: не ходіть в руст, там все погано
а в с++ вже придумали мільон костилів — бери і підставляй
Rust — завжди звертається до пам’яті, крім інлайнінгу
Дякую за пояснення, але як на мене, то це контролюється LLVM, а не Rust
наприклад переповнення значення знакового числа,
Навпаки, буде паніка при переповненні. Також є набір методів типу overflowing_add для операцій з переповненням.
не вірної роботи із системними визовами чи іншими С функціями
від такого ніхто не захистить, у тому числі С++
циклічною рекурсією і переповнення стеку
як і від цього
Clang так не робить, це саме фронтенд компілятора генерує а не middle-end. 2. Паніки там нема, отримаєте негативне число і це є документованою поведінкою. 3. С++ ніколи і не казали, що це нібито математично безпечна мова програмування. Від початку доказовість алгоритму перекладено на науковий метод — наприклад модульні тести. 4. Чим тоді Rust так сильно кращій за C++ 11+, може він мало не те саме тільки по іншому зроблене ? Контроль за часом життя об’єктів в динамічній пам’яті давно усіма робиться через інтелектуальні вказівники. Існують санітайзери і нщі тули по типу VTune чи Valgrind які покажуть утічку пам’яті якщо вона є. DeVirtualize at all transactions оптимізація існує в усіх відомих компіляторах більше 15 років і так далі. Rust просто відносно нова імперативна класична мова програмування із ML подібним синтаксисом. І взагалі якість і корисність програми швидше залежить від того хто, як, коли і для чого її написав, ніж від того якою мовою і за допомоги якої технології.
Rust придумали від же нєвіг дєлать, а тому що, походили по граблях С++,
Контроль за часом життя об’єктів в динамічній пам’яті давно усіма робиться через інтелектуальні вказівники. Існують санітайзери і нщі тули по типу VTune чи Valgrind які покажуть утічку пам’яті якщо вона є.
тільки це в русті робиться без тюна і варглінга та всяких статік аналіезерів
і що пропонує с++ замість
cargo fmt
cargo check
Ще забув про попабіль автомейк/смейк
2. Паніки там нема, отримаєте негативне число і це є документованою поведінкою.
Ви обидва праві. Паніка в дебаг-режимі і усікання в релізі.
Як на мене, це дурість, але вже зробили так.
онтроль за часом життя об’єктів в динамічній пам’яті давно усіма робиться через інтелектуальні вказівники.
Справа в тому, що коли в мові можна обійти щось — наприклад, замість unique_ptr, shared_ptr використат голі вказівники і напортачити з деструкторами — то це буде так, і в процесах звичайної фірми, де виділити навіть 5% часу команди на чистку старого боргу — це подвиг на межі неможливого («- Ти ж комуніст! — і кулемет застрочив знов») — це все накопичуватиметься.
А коли мова в принципі не дозволяє такого робити — то і накопичувати нічого.
із modern C++ 11+, то особливих переваг нема, скоріше є недоліки
Які, на вашу думку, ці недоліки?
0. С++ — безліч навчальних матеріалів, книжок, конференцій, записів і так далі. С++ дуже вивчена мова із міжнародним стандартом на неї. 3 широко використовуваних компіляторів — GCC, CLang та MS VC++ (що власне також і недолік). Величезна кількість готових для використання фреймверків для великої кількості бізнес доменів. QT, wxWidgets, Unreal Engine, Caffe і тому подібне. Багато IDE, санітайзерів по типу CppCheck, профайлерів типу Gproof, Vtune, Valgrind і т.п. Для комерційного проекту — це великий +, відносно легше зібрати команду, робити заміни, підвищувати кваліфікацію і т.п.
1. Головна риса С++ — це надмножина С, використання системного С коду це звичайна і стандартна опція, не траба робити нічого особливого по типу генерації чи ручного написання шиму скажімо до API операційної системи. Просто можна використовувати С-шні заголовки. В Rust — отаке маємо docs.rust-embedded.org/...rability/c-with-rust.html і те що займе в С++ хвилину, займе декілька годин в Rust.
2. Оптимізація наявними компіляторами і undocumented bechavior — С++ от тут youtu.be/fT3OALUyuhs?t=731 зібрали. Коротко — Rust далеко не такий вже і безпечний, а автомоатична оптимізація (яка людську ніколи не замінює, краше за кваліфікованого програміста вам ніщо не за оптимізує) — гірша, бо ніхто ще 30+ років не працював над такою оптимізацією, навідміну від С++ де до цього приклались: AMD, ARM,Borland, Digital Mars, Google, Intel, Microsoft, NVidea, IBM та інші.
en.wikipedia.org/...ompatibility_of_C_and_C+ Не на рівні API, швидше навмисно прибрані небезпечні від початку речі, які призводять до тяжких наслідків. Загалом же, абсолютна більшість C бібліотек і API може бути використана із C++ без будь яких додаткових дій зі сторони програміста, окрім підключення бібліотеки к проекту. Навідміну від Rust, C#, Java, Node, Python та низькі інших мов. Це дуже велика перевага із однієї сторони, але і небезпека з іншої.
1. Тобто, плюси краще, тому що популярніше. Згоден, але не завжди популяніший — це кращий, от складність Расту — це так, суттєва перепона.
2. Інтероп з сішкою у Расті теж є, але так, писати руками або генерувати
3. Раст може і не повністю безпечний (до того ж у ньому є ансейф, де можна робити майже що завгодно), але ж набагато безпечніший за сі. Ну і взагалі, най навіть на 30% повільніше за плюси, не у всіх проектах потрібна максимальна швидкість (тонни АПІшок на ноді і пайтоні тому доказом).
Не маю жодних заперечень, що Rust має місце бути. Дуже можливо якісь програми ним розробляти має сенс. Чи він є панацеєю від не ефективного програмування, людських помилок, потрібності в модульному тестуванні, санітайзінгу і таке іньше — відповідь чітка, ні ! За великим рахунком це щось на тему — а що краще, Kotlin чи Java ? Відповіді загалом нема, відповідь — на розсуд програміста. А коли архітект чи там тімлід обирають Rust чи Go чи D, чи Kotlin, Scala чи щось подібне до себе в проект, то це швидше за усе використовуються як засіб зібрати навколо проекту певний прошарок хакерів, а не кодерів за гроші. Тобто народу якому цікаво щось створювати з нуля, вивчати щось нове. А інколи просто охота по експериментувати — а раптом справді краще.
Архтек вибирає мову із інших міркувань.
Тімлід вибирає мову, рилі? Це де, в ТОВ «ратиці і хвости »?
Буває і таке, якось менеджер розв’язав руки у виборі тех стеку, тімлід вибрала node.js та neo4j. Не найкращій відбір був, навантаження не тримало.
112 коментарів
Додати коментар Підписатись на коментаріВідписатись від коментарів