VINONA
Many years ago I studied at the Polytechnic University and didn’t know exactly what to do. I dug into html, css and js, wrote several simple telegram bots in Python (they have since sunk into oblivion), then did the same in Java.

VINONA
I managed to do a lot of things, but I saw that I was stuck in the tutorial hell. I got a job as a frontend developer but the impostor syndrome got the better of me and I moved to customer support.

VINONA
During the 2020 pandemic, I found myself again in the Linux information bubble and took the deep dive. Since then I have been studying system administration and programming in the background.

VINONA
This year, I decided to find some community and consistent tasks. I searched for "web development for beginners" and found a mentor. At first we discussed coding React, but then the mentor asked what I was interested in, and I said that I was generally interested in software development, and shared my thoughts about rust and C++. The mentor suggested I take my dreams off the backburner, and I started writing Rust for the Retina project at Norcivilian Labs.

VINONA
Retina is a service for identifying retinopathy, an eye illness, from photos. The backend had already implemented the convolutional neural network, and we were writing the desktop and mobile client for it.

VINONA
At first I needed to set everything up to participate in the project which runs on Nix. I installed NixOS, configured it, and checked into the project on GitLab. I immediately ran into a bug with the cross-platform development shell and we had to fix the pattern matching in the Nix flake. After everything was up and running, I took on the first task — implementation of the login request.

VINONA
The REST API had already been implemented on the Retina server and our front-end engineer had already designed the screen layout. You can read his story here.

VINONA
Now I needed to create the right requests in Rust. I used the reqwest crate. crate is a library, just like using std::vector in C++. I created a request using the hashmap data structure built into Rust. The request endpoint accepted plain text, not json, so I converted the hashmap structure to a string. Then I made the request and processed the response. The reponse was given as a JSON structure, so I had to deserialize it. I created a Login struct and, output the response into it using the .json() method and returned the token string according to the documentation.

VINONA
When the task was complete, I ran the application to make sure it was working correctly and logged the response with the println! command, which printed the token to the system terminal. Since Retina runs on Tauri and uses a browser engine, I also logged the data in javascript developer console. Then I deleted all the lines for logs, committed changes and sent the merge request.

VINONA
At this point I started reading Programming with Rust by Jim Blendy - a really good book! The books are very dense and provide knowledge, but this is largely achieved at the cost of time.

VINONA
The next task I took was signup. This task was similar and I implemented it in the same way. On client side, the user fills out the registration form, the client calls my Rust function using the tauri invoke command, and passed the form input data. I receive the data from the client, deserialize it into a request body, and send a POST request using reqwest. In response I get data consisting of just one field “ok”, I deserialize it and return the result. Another task was ready, thus creating a new record on the server so that the user can signup.

VINONA
Here I encountered a 500 error, because the password I tested was too simple, and the server validation rejected it. I changed the password and everything was ready.

VINONA
Next was the user profile screen. I took the token from the client and had to get the user's profile data, so that when the client clicks on their profile, they see data about themselves. The story went the same way: I moved the old JavaScript code into the browser.js class and added a check that would call the new Rust code instead when on desktop. in Rust I created the function get_user_data, deserialized the token from the client, passed the required fields into the request body, including the token and operation name from the documentation. I received the response and deserialized it into a struct. Tauri takes care of the serialization of return values, so it arrived to the client as json. Another PR was ready.

VINONA
There is a meme in Rust that every codebase consists of unwraps. This is a method built into Rust that handles errors by panicking and closing the application. There is also a check that panics when a value inside the parentheses is not true or when the internals are not equal. This method is unsuitable for most cases because we don’t want the program to close, we want it to handle the errors in a try catch fashion. Rust has a special Result type for this, and some methods return a Result, which can be processed using pattern matching. We check whether it is ok or err, and either take the response value or handle the error. That’s why it’s called unwrap - we open, unpack the result and take the value. The Result type is generic and the type of the value inside it depends on the function that returns it.

VINONA
At first I wrote, so to speak, in draft mode, and used unwraps everywhere. Now I took up a task to refactor the code and add proper error handling. I changed all function signatures to return Result<string,string> and removed the unwraps. I created an intermediate variable to hold the Result, pattern matched using match, which is similar to the case switch in Java. If everything is ok, I write the value to another variable, and if an error comes, I return the error. Because of strict typing I make sure that the error type is a string. Now it is not the program that ends, but only the command. The user does not see the program crash, but sees an error inside the program itself. With that, the refactor was complete.

VINONA
My next task is to send the retinal image to the server for AI processing. The story here is the same, I make a request that, in addition to a token, accepts a picture and in response I receive data that I send to the client.

VINONA
Considering that I get pretty tired at my main job, I worked quite calmly and consistently. During this time, I managed to understand at my own level the basic concepts of Rust, such as ownership and borrowing, the features, the project itself in terms of what parst the code has, where to look, how the front end works, and the architecture. I use Linux, vim and bash for development and at home. I plan to continue putting different parts of Rust into practice, increase the complexity of the tasks, and get a better grasp of SQL and Docker, because they are usually asked in vacancies.

VINONA
Много лет назад я учился в Политехническом университете и уже думал о том, чем хочу заниматься, но не знал, чем именно. Поковырялся в html, css и js, написал несколько простых телеграм-ботов на питоне (с тех пор они канули в Лету), потом сделал то же самое на Java.

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

VINONA
Но во время пандемии 2020 года я оказался в информационном поле Linux и с тех пор сильно вник в Linux. В этом году ради общности и системности, чтобы делать задачи, а не придумывать их, потому что та или иная ответственность, я искал «веб-разработку для начинающих» и нашел наставника. Сначала мы обсуждали варианты реактивного кодирования, но затем наставник спросил, что меня интересует, и я сказал, что в целом интересуюсь разработкой программного обеспечения, а мысли о ржавчине/++ отложил в долгий ящик. Итак, я начал писать Rust для проекта Retina в Norcivilian Labs.

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

VINONA
Сначала мы работали с Nix и Linux. Для того, чтобы начать участвовать в проекте, нам нужно было всё настроить. Я установил NixOS, настроил его и зарегистрировался в нашем проекте на GitLab. Мы сразу же столкнулись с ошибками и добавили сопоставление с образцом в Nix-флейк для запуска оболочки кроссплатформенной разработки.

VINONA
После того, как все было сделано и запущено, я взялся за первую задачу — реализацию входа в систему.

VINONA
REST API уже реализован на серверах Retina, и наш интерфейсный инженер уже разработал первоначальный макет. Его историю можно прочитать здесь. Теперь мне нужно было создать правильные запросы в Rust. Я использовал crate reqwest. crate — это аналог библиотеки, точно так же, как используется например std vec в C++. Я создал запрос из сериализованной встроенной структуры данных хэш-карты в Rust. Я провел сериализацию, так как запрос принимает текст, я преобразовал структуру хэшмапа в строку. Потом я отправил этот запрос и получил обработанный ответ. Дальше я провел сериализацию, создал структуру для логина и с помощью метода json превратил ее в структуру, и функция вернула токен, как и требовала документация.

VINONA
Задача была готова, я проверил ее, запустил приложение, убедился, что оно работает, записал состояние с помощью команды println!, которая вывела все в системный терминал. Поскольку Retina работает на Tauri и использует браузерный системный движок со встроенной клиентской консолью, я также записывал туда данные. Затем я удалил все логи, закоммитил и отправил мерж-реквест.

VINONA
В этот момент я также начал читать «Программирование на Rust» Джима Бленди, действительно хорошую книгу. Книги очень плотные и дают знания, только это во многом достигается ценой времени.

VINONA
Следующее задание, которое я взял, — это регистрация.

VINONA
Эта задача была аналогичной, ее реализация была организована технически и организационно одинаково. Я создал функцию, которую вызываю из клиентской части с помощью команды вызова, так устроен Таури, и я должен передать данные в эту функцию. В клиентской части пользователь заполняет данные, из формы я передаю их на бэкенд и сразу десериализую эти данные в Rust. Я получил данные со стороны клиента, создал запрос по тому же запросу, провел сериализацию по тому же шаблону сопоставления, обработал ошибку и отправил POST-запрос, передав ему тело запроса. И тогда в ответ я получаю данные, состоящие всего из одного поля «ок», но мне еще нужно десериализовать это поле, обработать обработку ошибок и вернуть результат. На этом этапе была готова еще одна задача, я прописал команду Tauri в lib.rs, создал функцию, сделал GET-запрос через клиент блокировки reqwest, получил JSON, сделал обработку ошибок, взял string.status("ok"), залогинился и, таким образом, создали новые данные, чтобы пользователь мог войти в систему.

VINONA
Здесь я столкнулся с ошибкой, я все залогинил, все проверил, все работало, кроме одного — вернулась ошибка 500, потому что внутри нашего бекенда есть логика, которая проверяет пароль, а пароль был слишком простым, поэтому сервер вернул ошибку. Я сменил пароль и все было готово.

VINONA
Далее был профиль выборки.

VINONA
Я взял токен у клиента и получил данные профиля пользователя, чтобы, когда клиент нажимает на профиль, он видел данные о себе. История была та же самая: я переместил старый код JavaScript в браузер.js и создал фасад в api.js, который вместо этого будет вызывать новый код Rust. в Rust я создал функцию get_user_data, клиент отправил ей токен, функция получила токен, десериализовала его, создала хеш-карту. Я сверился с документацией и передал в тело запроса необходимые поля, включая токен и имя операции. Я получил ответ и структурировал его в нужную структуру. Здесь я уже вернул структуру в виде json, поэтому я возвращаю структуру, а Таури позаботится о сериализации, поэтому она поступает клиенту как json. Очередной PR был готов.

VINONA
В Rust есть мем, что весь код состоит из развёрток, unwrap. Существует проверка, которая вызывает панику, если значение внутри скобок не соответствует действительности. Еще есть проверка, которая паникует, если внутренности не равны. Есть проверка, которая паникует, если у меня ошибка. В Rust для этого существует специальный тип результата, и некоторые методы возвращают результат, который я обрабатываю, используя сопоставление с образцом. В Result есть дженерик, я проверяю его с помощью ok err, вижу, что это такое, возвращаю только то, что мне нужно. Также есть ожидание, когда я также создаю панику, но когда выходит значение «err», ошибка того или иного рода, я вызываю панику и пишу сообщение внутри параметра ожидания.

VINONA
Сначала я писал, можно сказать, в тестовом режиме, везде использовал unwraps — встроенный в Rust метод, который обрабатывает ошибки путём паники и закрытия приложения. Этот метод в большинстве случаев непригоден, поскольку мы не хотим, чтобы программа закрывалась, мы хотим, чтобы она их обрабатывала по аналогии с try catch.

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

VINONA
Многие методы языка Rust возвращают результат или опцию. Разработчики Rust сделали это для того, чтобы когда я возвращаю тип, который не готов, я хочу получить строку, в ответ приходит строка, Rust возвращает вариант, который мне нужно расширить. поэтому это и называется unwrap - открыть, распаковать. Соответственно, вместо нашего итогового типа данных я меняю тип на Result и убираю развертку и то значение, которое я получил, тоже делаю опцию. Наше промежуточное значение — это не строка, а Result<string, string>. Я создаю промежуточную переменную, в которую записываю Результат, записываю конечную переменную, беру тег, инструкцию, тег как в других языках, например в языке Java есть такое понятие, как переключатель регистра, что То есть тег также является случаем переключения, когда я должен получить от него результат. Если все "ок", записываю в переменную, если приходит ошибка то не записываю ошибку в конечную переменную, конвертирую к нужному типу так как в расте строгая типизация, мне обязательно нужна строка, однако при в этот момент я пишу возврат и наша команда просто завершается на этом этапе. Преимущество здесь в том, что завершается не программа, а только команда, пользователь не увидит, что программа упала, он увидит ошибку внутри самой программы. Наконец, рефактор был готов.

VINONA
Моя следующая задача — отправить картинку, потому что Retina устроена таким образом, что я отправляю изображение сетчатки на сервер, и оно передается ИИ. История та же, делаю запрос, который помимо токена принимает картинку и в ответ получаю данные, которые отправляю клиенту.

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

VINONA
Я использую Linux для разработки, дома использую vim и bash. Прежде всего, мне нужно продолжать применять разные части Rust на практике, усложнять задачи, в идеале, лучше знать sql и Docker, ведь их обычно просят или дают как плюс.

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

🔗