Pull to refresh
-1
Send message

Мне одному кажется, что листинг 24, где "раскрыли" цикл for отработает неправильно если количество фигур < 4?

А так статью видел, про нее много было сказано и без меня

Интересно, за счет чего такой отрыв от Deno? Надеюсь, разработчики динозавра будут мотать на ус

Всех заинтересованных поздравляю, конкуренция на данном поприще только пойдет на пользу

Единственное, что не нравится - смешивание CommonJS и Modules. Как заметили выше, это только усугубит текущую ситуацию

Я не очень разбираюсь в C++, но поржал, спасибо

fn factorial(n: u32) -> u32 {
    if n == 0 {
        return 1;
    }
    n * factorial(n - 1)
}

Тут ничего необычного, лучше избавляться от вложений, если это возможно (хвостовые рекурсии пока в расчет не берем).

Это связано с тем, что исключения могут привести к утечкам памяти и другим проблемам. Кроме того, исключения могут быть трудными для понимания и отладки.

Чо

Вместо исключений Rust использует два типа ошибок:

Option - возвращает значение типа T, если оно доступно, или None, если оно недоступно.

Чо. С каких пор Option является типом ошибки?

Между Eq и PartialEq на самом деле есть разница. Если интересно, это обсуждали, например, здесь: https://users.rust-lang.org/t/what-is-the-difference-between-eq-and-partialeq/15751/11

Один из способов неправильного использования generics - это пытаться написать код, который работает для всех типов данных. Это может привести к коду, который сложно поддерживать и расширять.

Может, я не прав, но это в корне не верно. В данных примерах мы используем примитивы, но если представить, что у нас более сложные типы, то намного лучше предоставить реализацию по умолчанию с T: Trait1 + Trait2, а там, где необходимо предоставить другие трейты или типы. Таким образом, например, реализованы типы из std (Box, например).

чтобы обойти систему типов и безопасности

Ключевое слово unsafe ничего не выключает, чтобы называть это обходом. Например, мы можем сделать каст &T => *mut T, но обращение к нему все еще может быть только в unsafe блоке.

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

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

Здравствуйте, я извращенец уже несколько лет и хотел бы знать, что вас не устраивает в синтаксисе и концепциях языка

Как бонус, прошу привести пример языка с хорошим синиаксисом и помечаниями, почему он хорош, спасибо

Не ясно откуда такой оверхед (может это из-за логов, которые вы почему-то оставили?), у меня решение на Rust справляется также быстро и потребляет максимум 2.2mb (0ms 2.1mb, 4ms 2.2mb).

Также, не думаю, что \n и \r - легальные символы. Но они, собственно, в коде и задействуются как условия завершения работы.

Может case '1' ... case '9' стоило поместить в default ветку и сравнить два раза (с '1' и '9' соответственно)?

Посмотрел на функцию number_exponenta_calc и заметил, что вы производите много работы с числами. Не думаю, что это хорошо, потому что от вас этого не требуют. Условно, если добавить этот код в какой-то проект (просто представьте), то число будет вычисляться дважды - для проверки и при построении после.

Кому интересно, код на Rust можно посмотреть тут

Не заметил, на самом деле, но если есть что ДОБАВИТЬ - прошу

Взрослым (=опытным, не ейджизм) можно ориентироваться на кодстайл в своих кодовых базах. Например, я люблю цепочки из функциональных вызовов map.skip.filter.take (с отступами, конечно, не в линию же), а также сам отступ у меня 2 (с тупескрипта зашло)

Причем тут модель памяти в Rust? Речь же про неопределенное поведение.

В Rust не может существовать две мутабельные ссылки на один объект, но могут быть два мутабельных указателя на один объект.

полного списка UB в расте вы нигде не найдёте

Дарю: https://doc.rust-lang.org/reference/behavior-considered-undefined.html

Если мы говорим про UB, то нужно делить Rust на safe и unsafe. UB в safe не существует. Список вещей, которые считаются UB предоставлен.

Во вторых в расте столько же точек отстрела + свои ещё

Как хорошо, что это неправда

Я так понимаю, на этом мы заканчиваем

В Rust есть два типа строк - &str и String. Первый представляет собой ссылку на строку, второй - владеющую строку.

Если это туториал, то, думаю, стоит упомянуть, что &'_ str представляет собой по сути структуру с указателем на начало и длиной строки где-то в памяти (будь то стэк или куча).
А вот String содержит Vec<u8> и не только владеет строкой, но и всегда держит ее в куче.

Однако на практике проще начинать с String. Этот тип проще использовать, он ведет себя схожим с привычной строкой в Java. А &str требует следить за владением и ссылками

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

В данном примере можно было бы спокойно использовать примитивный тип str.

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

Единственное, что нам необходимо - нечто, что можно отобразить. За эту характеристику отвечает трейт Display. Вот как измениться функция:

fn print_greeting<T: Display>(name: T) {
    // Сейчас можно писать так
    println!("Hello, {name}!");
}

fn main() {
    let name = "John".to_string();

    print_greeting(name.as_str());  // Или &name - это &str
    print_greeting("Sam");          // это тоже &str
    print_greeting(name);           // Это просто String

    // Обычно потом от строк отходят в пользу более сложных структур
    // В вашем варианте придется везде писать .to_string()
    // Здесь же достаточно, чтобы структура реализовала Display
    print_greeting(person);         // или &person
}

Второй совет тоже плохой:

Вы упоминули Debug, но не остальные трейты. Даже Clone забыли, без которого не получится скопировать сложные структуры (о Copy и других даже не заикаюсь).

Третий совет еще хуже чем первый:

Когда вы используете владеемые типы вы ЯВНО управляете памятью, потому что вместе с выходом из функции начнутся ненужные удаления объектов. Идея простая - смотрите на то, как вы используете объекты. Ничего страшного в лайфтаймах нет, а с одним аргуметом нет никакого смысла избегать их (где запутаться хоть можно?).

Четвертый совет мимо кассы:

Если читатель не захочет использовать структуры, откуда он возьмет классы? В Rust их нет.

Нормальный совет мог бы звучать так: используйте композицию объектов - аналог наследования, и создавайте собственные трейты - аналог интерфейсов. Сразу отмечу, что второй более гибкий и простой на практике.

Вместо конструктора реализуют функцию new.

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

use std::sync::Arc;

// Классов в Rust нет
struct Struct {
    pub field: i32
}

impl Default for Struct {
    fn default() -> Self {
        Self { field: 42 }
    }
}

fn main() {
    let data: Arc<Struct> = Arc::default();
    println!("Field equals to '{}'", data.field);
}

Пятый совет тоже неудачный:

Поскольку Result - тип с дженериками, разные Result плохо между собой уживаются и требуется постоянно маппить ошибку. Используйте крейтыanyhow и thiserror для данных целей (смотрите документацию - оно того стоит).

// Добавьте крейт anyhow к существующему проекту:
// cargo add anyhow --features backtrace
//   По умолчанию без бэктрейса

// Скрываем наш Result
use anyhow::Result;

// Используем derive macro Error из thiserror
// При желании, имя можно поменять
use thiserror::Error;

#[derive(Error, Debug)]
#[error("Unable to divide by zero denominator")]
pub struct DivisionByZeroError;

fn divide(num: i32, den: i32) -> Result<i32> {
    // Для деления с проверкой лучше ее и использовать
    num.checked_div(den).ok_or(DivisionByZeroError.into())
}

// Наиболее желательная сигнатура, anyhow Error можно использовать здесь
fn main() -> Result<()> {
    println!("{}", divide(1, 0)?);

    Ok(())
}
    Blocking waiting for file lock on package cache
   Compiling playground-rust v0.1.0 (...)
    Finished dev [unoptimized + debuginfo] target(s) in 1.26s
     Running `target\debug\playground-rust.exe`
Error: Unable to divide by zero denominator

// Если включена фича backtrace в anyhow
Stack backtrace:

В качестве типов для ошибок лучше всего использовать что-то, что реализует трейт Error, иначе придется маппить ошибки. Два эти крейта делают все за вас.

Использование ? вместо unwrap() для эскалации ошибок

Для чего - для чего unwrap используется? Для "эскалации" ошибки? Мы точно про один Rust говорим?

И опять же, можно переписать на нормальный код с anyhow.

Далее идут два нормальных совета, но есть примечания:

В Java для объявления ошибок используется Exception. В Rust такого нет.

" Да ладно?! " (c) Якубович

А зачем тогда нужен трейт std::error::Error, который используется в thiserror и anyhow, и который, к тому же, можно вернуть в виде Box<dyn std::error::Error> из функции main (тем самым позволяя пробросить ошибку откуда угодно на самый верх)?

В Java, когда вы хотите, чтобы переменная была доступна в нескольких потоках для чтения вы просто передаете ссылку. 

// Не показывай этот код друзьям
struct SomeStruct {
    pub thread_unsafe: String
}

unsafe impl Send for SomeStruct {}
unsafe impl Sync for SomeStruct {}

// SomeStruct можно использовать без Arc

Отличие Java от Rust здесь в том, что Rust говорит все типы нельзя безопасно передавать Send или использовать из разных потоков Sync, если не будет сказано обратное. Оба эти трейта - маркеры, поэтому их не стоит реализовывать не убедившись, что интерфейс типа не является потокобезопасным.

Я не так хорошо знаком с Java, но думаю там все +\- также: можно передать объект в другой поток и получить одновременный доступ к одному элементу

Кстати лучше сразу использовать futures::lock::Mutex, а не std::sync::Mutex, так как второй спроектирован для не асинхронного кода

Не пробовал, вместо этого советую сразу async_std - там есть многие асинхронные реализации совместимые с синхронным std (по крайней мере, когда я писал ВКР, проблем не ощутил).

Используйте RwLock, когда необходимо получить множественное чтение (одним "мутиксом" сыт не будешь).

Еще одним интересным аспектом перехода с Java на Rust является использование трейтов (traits). В Rust нельзя объявить асинхронные функции внутри трейтов напрямую.

Можно. Для справки сейчас ведется работа над стабилизацией асинхронных трейтов. А данный макрос просто делает то, что можно уже сейчас с использованием сопли на конце: ... -> Pin<Box<dyn core::future::Future<Output = ()> + Send + '_>> {...}

Собственно, async - это сахар, который не стабилизирован для трейтов.

Что там с выводами?

  1. При переходе с Java на Rust лучше ориентироваться на то, что ДЕЛАЕТ тип, для чего он НУЖЕН в данной функции и отталкиваться от его функциональность. Именно ЭТО позволит быстрее написать рабочую версию кода.

  2. При разработке ориентируетесь на трейты, а структуры воспринимайте как конкретизацию их набора.

  3. Для обработки ошибок действительно используется Result, но ничего не запрещает заткнуть все Option (они тоже поддерживают ?). Лучше всего использовать Result из anyhow и приправить щепоткой thiserror.

  4. Все крейты упрощают разработку, равно как и для другого языка и его пакетов. Что более важно, ориентируйтесь на МАКРОСЫ. Пишите их, используйте их. И будет вам быстрая разработка.

По поводу пункта 4. Лучшим примерами будут: serde (и связанные библиотеки serde_yaml), thiserror, clap (3 версия, питоновский click). И наверное есть много других - эти первые на ум пришли.

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

Это просто ужас. Надеюсь, ПЕРВЫЙ блин коммом. А дальнейшие статьи будут значительно лучше

P.S. Комментарий писался с перерывами суммарно около 2 часов. Я хотел уточнить некоторые вещи, но решил упростить комментарий и сконцентрироваться на самой статье. Спасибо за внимание

Ну-ка, давайте вдаваться. А не то ваш комментарий как нонсенс выглядит

В том числе. Это был бы очень хороший опыт

Советую всем к ознакомлению с серией статей от Энтони Фу

https://antfu.me/posts/ai-qrcode

Касаемо статьи: много return, которые можно опустить.

// Зачем нужна эта функция?
fn create_reed_solomon_encoder(num_error_correction_blocks: usize) -> Encoder {
        let reed_encoder: Encoder = Encoder::new(num_error_correction_blocks);

        return reed_encoder;
}

impl Iterator for ZigZagIt {
    type Item = (usize, usize);
    fn next(&mut self) -> Option<Self::Item> {
        if !self.valid { return None; }

        let cordinates: (usize, usize) = (self.row_cordinate, self.column_cordinate);

        self.move_cordinate();
        // Зачем return?
        return Some(cordinates);
    }
}

Если необходимо было освоиться в Rust, то считаю, что лучшим решением было бы написать крейт с нуля

Смотрю на зоопарк веб фреймворков и не понимаю, зачем столько. В рамках эксперимента - пожалуйста, но чем Solid & Qwik отличаются от React, а Svelte от Vue?

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

По большому счету, я вижу только путь шаблонов (Vue / Svelte) и путь функциональных компонентов (React / AnyOtherFramework). При этом, например, во Vue можно использовать функциональные компоненты и я теперь уже вообще не понимаю, что, куда и почему

Не поверил своим глазам, как раз искал статьи про устройство Regex для решения одной задачи с LeetCode. Спасибо огромное

Он обычный, си-подобный. Лямбды мне из C++ больше заходят, но в Rust они компактные. В остальном вообще ничего сказать не могу. В чем громоздкость - хз

Я только не понимаю, почему все считают синтаксис Rust сложным? Единственное, чем у меня была проблема - турбофиш

Какой смысл в таких рекрутерах? Ничем от ботов не отличаются - очень сложно пробиться, без негатива

Information

Rating
Does not participate
Location
Липецкая обл., Россия
Date of birth
Registered
Activity

Specialization

DevOps-инженер
Python
PostgreSQL
SQL
Git
C++
Qt
Rust
Django
RESTful API