Pull to refresh

Comments 17

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

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, то считаю, что лучшим решением было бы написать крейт с нуля

Спасибо за комментарий! В моём коде я хотел правильно делить функции, но я кажется перестарался. Return же я просто привык писать, но кажется стоить себя переучивать. В следующий раз я исправлю эти недочёты.
Про создание своего крейта, Вы это про алгоритм Рида Соломона?

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

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

Ещё б неплохо почитать про match и про DRY.

Пелёнка превращается
 fn generate_bitvec(integer: u32) -> BitVec {
        let bit_len = |num: u32| {
            if num > 99 {
                return 10;
            }
            else if num > 9 {
                return 7;
            }
            else {
                return 4;
            }
        };

        let mut bitvector: BitVec = BitVec::new();
        bitvector.reserve(bit_len(integer));

        append_to_bitvec(&mut bitvector, &integer, bit_len(integer));

        return bitvector;
}

В элегентную функцию
fn generate_bitvec(num: u32) -> BitVec {
        let bit_len = match num {
          _ if num > 99 => 10,
          _ if num > 9 => 7,
          _ => 4,
        };
        
        let mut bitvector = BitVec::with_capacity(bit_len);
        append_to_bitvec(&mut bitvector, &integer, bit_len);
        bitvector
}

И правда, выглядит очень хорошо, спасибо за пример, я обязательно почитаю про это.

А мне нравится писать return в rust)

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

Да, конечно: https://pionir.org. Это Сербская школа которая обучает нас, в основном с помощью практики. Мы много чем занимаемся, и пытаемся улучшить окружение око нас.

Сайт чуть-чуть не информативен, поэтому разрешите задать вопрос: со скольки и до скольки лет принимаются заявки? Сайт на сербском без возможности выбора языка, значит ли это то, что нужно знать сербский? На каком языке говорят в вашем чате?
И, как я понял из обрывков информации, там кроме программирования ещё проектируют устройства на печатных платах?

Да, сайт и правда не информативен, так-как староват, мы сейчас создаём новый.
Пока что здесь принимаются дети которые знают Сербский и Английский, но если что, здесь есть люди которые говорят на нескольких языках, например наш директор знает Русский.
Заявки же принимаются с 13 до 17 лет, но с идеей что ученики останутся и дальше в школе.
Кроме программирования мы много чем занимаемся, например: электроника, дизайн, этика, право, социология, экономия, и даже садоводство в каком-то смысле, всё это учится вместе, интегрировано в остальные моменты. Но ученик сам выбирает свою сферу обучения, по своему желанию и возможностям.
Надеюсь это немного прояснило про нашу школу.

А мне нравится этот язык. Читабельность у него ниже, чем у Java, но зато он потенциальный заменитель C/C++. Если ещё сделают грамотную обработку исключений в нём, чтобы не было таких баг вроде "На 40-ой минуте звонка стабильно у абонентов пропадает звук", из-за того, что переменная переполнилась и обнулилась, то будет совсем замечательно. И Race Condition под нагрузкой с битьём памяти самый ад, или разная работа программы на разных девайсах. Или уязвимости. Если всё это смогут сделать, то C/C++ окончательно отправятся на пенсию и будут там сидеть вместе с языком Ada,Fortran-ом и Cobol-ом.

Для того, чтобы ещё лучше понять, что происходит на низком уровне, я бы порекомендовал хорошенько разобраться в C. Не в C++ (это плохой язык), а именно в С. Этот язык прост в синтаксических конструкциях, но очень сложен в написании отказоустойчивого кода. Нужно очень серьёзное понимание компьютера, чтобы успешно писать на нём. Несколько мощных выстрелов себе в ногу как раз и помогут понять, зачем был создан Rust, зачем нужно и очень полезно испытывать боль от Borrow checker'а, зачем все эти церемониальные практики языка.

Спасибо за совет! Скорее всего это будет хорошей идеей, после Rust я начал понимать много чего, но скорее всего и C даст мне огромные знания.

Понимаю, что это, возможно, первый код автора на Rust, но всё-таки хочется указать на некоторые недочёты.


Непонятен выбор библиотек. Почему generic-matrix вместо ndarray или nalgebra? Почему именно reed-solomon? Так вышло, что я знаком с автором reed-solomon, и он говорит, что эта библиотека была в своё время его упражнением в Rust и не предназначена для серьёзного использования.


Теперь непосредственно по коду.


impl Module {
    pub fn set_module(&mut self, new_module: Module) {
        *self = new_module;
   }

Этот метод просто не нужен и целиком заменяется на оператор присваивания по месту.


   pub fn is_fun(&self) -> bool {
       match self {
           Module::Function(_) => return true,
           _ => return false,
       }
   }

Можно написать короче:


    pub fn is_fun(&self) -> bool {
        matches!(self, Self::Function(..))
    }

    pub fn flip_module(&mut self) {
        match self {
            Self::Data(true) => *self = Self::Data(false),
            Self::Data(false) => *self = Self::Data(true),
            _ => (),
        }
    }

Опять-таки, можно написать короче и заодно не расписывать все варианты:


    pub fn flip_module(&mut self) {
        if let Self::Data(data) = self {
            *data = !*data;
        }
    }

pub fn set_square(&mut self, size: usize, cordinate: (usize, usize), matrix_module: &mut Matrix<Module>) {
        let start_width: usize = cordinate.1;
        let start_height: usize = cordinate.0;

        for i in 0..size {
            for j in 0..size {
                let new_module = matrix_module.index_mut((i, j));
                self.set_module((i + start_height, j + start_width), *new_module);
            }
        }
   }

    pub fn set_module(&mut self, cordinate: (usize, usize), new_module: Module) {
        let mut _module: &mut Module = self.get_mut_module(cordinate);
        _module.set_module(new_module);
}

К этим функциям у меня хватает вопросов:


  1. Зачем отдельным параметром передается size, если, насколько я понимаю, можно вынуть размер из matrix_module?
  2. Зачем matrix_module передаётся по мутабельной ссылке, если он не меняется?
  3. Зачем используется index_mut вместо оператора индексации?
  4. Почему в set_module выносится отдельная переменная _module, да ещё и с прочерком в начале? В Rust так принято именовать неиспользуемые переменные, поскольку по умолчанию на них не срабатывает предупреждение о мёртвом коде.

(и, кстати, почему cordinate с одной o?)


И да, библиотеки рулят. С какой-нибудь nalgebra весь метод можно было бы свести к


pub fn set_square(&mut self, at: (usize, usize), square: &DMatrix<Module>) {
    self.module.view_mut(at, square.shape()).copy_from(square);
}

Далее, функцию create_reed_solomon_encoder можно без ущерба для кода заменить на Encoder::new. Это даже короче.


pub fn create_error_corrections_blocks(data: Vec<u8>, num_error_correction_blocks: usize) -> Vec<u8> {
        let reed_encoder: Encoder = Self::create_reed_solomon_encoder(num_error_correction_blocks);

        let data_copy = reed_encoder.encode(&data).to_vec();

        return data_copy;
}

Разнесено на три строчки то, что можно уместить в одну:


pub fn create_error_corrections_blocks(data: &[u8], num_error_correction_blocks: usize) -> Vec<u8> {
    Encoder::new(num_error_correction_blocks).encode(data).to_vec()
}

И да, функция зачем-то принимает на вход Vec<u8>, вынуждая вызывающую сторону безусловно выделять память, в то время как просто слайса (&[u8]) было бы достаточно (собственно, это именно то, что принимает на вход Encoder::encode).


В функции move_horizontaly есть число 6. Мне, как человеку со стороны, вообще непонятно, откуда оно взялось.


В функции move_vertical есть вот такой странный фрагмент:


        else {
            if self.upward { self.row_cordinate -= 1 }
            else if !self.upward { self.row_cordinate += 1 }
            self.column_cordinate += 1;
        }

Зачем тут else if? Условия self.upward и !self.upward взаимно исключают друг друга.


В любом случае, не смотря на недочёты, видно, что автор программировать всё же умеет — что правда впечатляет с учётом его возраста. Правда, у меня сложилось впечатление, что хоть это и первая программа на Rust, но не первый опыт программирования у автора в принципе. Судя по косвенным признакам (аннотации типов, геттеры, сеттеры, ненужные return), я бы сделал ставку на Java. Я угадал?


И да, хотелось бы увидеть опубликованный код, желательно на платформе, где можно предложить исправления. Кода в статье не хватает для того, чтобы собрать программу целиком.

Большое спасибо за такой подробный комментарий!

>>Непонятен выбор библиотек. Почему generic-matrix вместо ndarray или nalgebra? Почему именно reed-solomon? Так вышло, что я знаком с автором reed-solomon, и он говорит, что эта библиотека была в своё время его упражнением в Rust и не предназначена для серьёзного использования.

Так-как я только начал программировать на Rust, я достаточно небрежно выбирал библиотеки, в следующий раз я буду смотреть более детально.

>>Опять-таки, можно написать короче и заодно не расписывать все варианты:

Я видел разные фишки, как сделать код в Rust более коротким, и так код выглядит намного лучше, за следующий проект я сделаю код лучше и читабельнее чем сейчас.

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

>>>И да, библиотеки рулят. С какой-нибудь nalgebra весь метод можно было бы свести к

pub fn set_square(&mut self, at: (usize, usize), square: &DMatrix<Module>) {
    self.module.view_mut(at, square.shape()).copy_from(square);
}

Спасибо за библиотеку, скорее всего она будет очень полезна в следующих проектах.

>>В функции move_horizontaly есть число 6. Мне, как человеку со стороны, вообще непонятно, откуда оно взялось.

Да, я понимаю что в моём коде многовато магических чисел, в этом случае, столбец под номером "6" имеет все функциональные блоки, так-что его надо всего лишь обойти.
Я не особо хорош пока что в наименованиях, поэтому я оставил так, в QR кодах есть много разных таких чисел, которые были выбраны создателями QR кода, но наименование тяжело выбрать.

И да, я правда много программировал и до Rust-а, но в основном на Python и C#, хотя на C# я программировал очень мало, но привычки остались.
Вот мой GitHub, там можете посмотреть мои проекты на Python и целый код генератора. Там не все мои проекты, я занимался много чем, но не всё выставлял в GitHub.

Sign up to leave a comment.

Articles