All streams
Search
Write a publication
Pull to refresh
88
0
Александр Мещеряков @freecoder_xx

Rust разработчик

Send message
Развитая статическая система типов с автоматическим выводом и удобное управление зависимостями. Для скриптовых целей — минимализм и удобство, близкое к динамически типизированным языкам, но с сохранением строгой статической типизации. Для некоторых — это недостаток, для других — киллер-фича.
Я использовал в своем Rust-проекте небольшие скрипты, написанные на Rust. Довольно удобно, когда с растом освоился и вникать в питоновские либы нет ни времени, ни желания.
В случае Rust у меня будут проблемы только если я захочу иметь возможность что-то мутировать одновременно? Но не будет если я передам ссылку на то, что мутирую, последовательно? Или вообще на каждом шагу компилятор будет бить меня битой по голове?

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


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


Если же вам такое нужно постоянно, то придется использовать контейнерные типы для внутренней изменяемости, такие как RefCell:


use std::rc::Rc;
use std::cell::RefCell;

struct MyStruct {
    a: Rc<RefCell<i32>>,
}

fn main() {
    let a = Rc::new(RefCell::new(0));
    let s = MyStruct { a };

    let b = s.a.clone();

    *s.a.borrow_mut() += 1;
    *b.borrow_mut() += 1;

    println!("{}", s.a.borrow());
}

Запустить


Как вы можете видеть — это больно, но терпеть можно :)

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


В противном случае вам придется:


  1. Использовать Rc, RefCell и прочие Weak; или
  2. Использовать сырые указатели и управлять освобождением памяти вручную; или
  3. Хранить узлы во внешнем контейнере и ссылаться на них по индексу; или
  4. Использовать готовые библиотеки, которые скрывают всю кухню за своим API.

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


Собственно, из-за чего вознрикает проблема с графовыми структурами?


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


  1. Объект должен быть доступен из разных мест по ссылке, и удален только тогда, когда на него нет ссылок. Если есть по крайней мере одна ссылка, то объект должен жить.


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



Первая проблема решается подсчетом ссылок (reference counting) и в других популярных прикладных ЯП она обычно скрыта от пользователя за реализацией. То есть любая "ссылка" на объект в таких ЯП всегда есть умный указатель со счетчиком ссылок. В Rust же такого типа ссылку нужно создавать вручную, если она вам нужна. Для этого используются типы Rc и Arc. Кроме того, нужно как-то решать вопрос образования циклических ссылок, для чего используется Weak.


Вторая проблема решается введением совместно используемых изменяемых контейнеров — это типы Cell, RefCell, Mutex, RwLock.


Соответственно, ссылки с типами вроде Rc<RefCell<Node>> всех и пугают.


Для большего понимания я рекомендую прочитать вот эту страницу официальной документации и разобрать приведенный там пример с гаджетами для того, чтобы понять, для чего и как использовать Rc, RefCell и Weak вместе. Также рекомендую ознакомиться с документацией по cell.

Rust трудночитаем только для тех, кто на нем не программирует. Потому что синтаксис непривычный. Но он довольно простой и проблем с восприятием чужого кода, если он не перегружен лайфтаймами (что бывает редко), обычно не возникает. Читать исходники зависимостей проекта — обычная практика в Rust. Часто это быстрее и проще, чем смотреть документацию.
Вам половину ревью сделают компилятор, rustfmt и clippy. Писать на Rust новый код чуть долше, но вот рефакторить ранее написанный — довольно быстро и приятно. А главное — появляется уверенность в его надежной работе с технической стороны, можно больше внимания уделять проверке логики (даже по сравнению с Java, где у меня на большом проекте примерно 80% времни уходило на исправления NPE-багов).
Использовать трейты и структуры — вполне себе идиоматично. А вот трейт-объектов старайтесь избегать: применяйте их только там, где действительно другие способы не работают.
такие как Rust,… — тоже выдают предупреждения, где это возможно

В Rust будет не предупреждение, а ошибка компиляции, если вариативный анализ тип-суммы окажется неполным.

На каком языке программировать?
Интересно, мы давно играем в такую игру в своем семейном кругу. Назвали ее — «будем знакомы». Это просто карточки с образными высказываниями («интересный человек», «первая встреча с велосипедом», «все вместе» и т.д.), которые задают тему, по которой вытащивший карточку человек обязан высказаться. Потом остальные тоже высказываются, по желанию. И дальше тянет карточку следующий по кругу. Очень хорошо работает в малознакомой компании или при отсутствии хороших тем для общения. Про игру «две правды — одна ложь» не знал, спасибо, возьму на вооружение.

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


#[derive_methods(Foo)]
struct MyStruct;

Потому что с точки зрения макроса, Foo — это path, а не полноценная структура/типаж с объявленными методами, доступа к внутренностям Foo макрос не имеет.

Нет, скорее будет так: custom_tcb.show(), внутри него self.tristate_cb.show(), внутри него self.cb.show() — и так далее.

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


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


Другая проблема — раздувание классов. В простейшем случае, у вас есть типы Rectangle и Square. И по логике Square должен быть подтипом Rectangle, но последний для хранения своего состояния требует больше памяти, чем первый (a + b против просто a). В итоге, применяя наследование для образование подтипа, вы чрезмерно раздувает Square. И когда у вас глубокая иерерхия наследования, это делает ваши объекты просто монструозными.


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

Решение: Rc + RefCell + Weak.

Хотя Option почему-то является изменяемым

Нет, не Option является изменяемым, а метод take вы можете вызвать только на изменяемом объекте. Потому что:


pub fn take(&mut self) -> Option<T> {
    mem::replace(self, None)
}

take заменяет значение на None, что невозможно сделать на иммутабельном объекте.

Просто отборнейший трэш! ))
Современный отечественный цирк, безвкусица и эпатаж массовой культуры, доведенные до абсурда.

Это все сделать конечно можно, но кто будет кормить разработчиков, пока они все это будут разрабатывать, поддерживать и наполнять? )

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

"Но при этом сохранив сигнатуру annotate как она есть. То есть вернуть ссылку с подсчётом ссылок (например, Rc<Wrapper>) — не подходит."


В каких случаях это может быть необходимо? Когда мы можем использовать Box, но не можем Rc потому что… не хотим менять сигнатуру метода? Да ладно? )

Спасибо! С картинками воспринимать обзор стало значительно легче )
Радует, что проводится столько конференций по Rust и созревает экосистема.

Information

Rating
Does not participate
Location
Санкт-Петербург, Санкт-Петербург и область, Россия
Registered
Activity