Задать вопрос
Ответы пользователя по тегу Rust
  • Почему wasm код не генерируется при компиляции rust кода с сырыми указателями?

    bingo347
    @bingo347
    Crazy on performance...
    Начну пожалуй с того, что в первом примере у Вас UB (undefined behavior):
    #[no_mangle]
    pub fn test() {
        // Вот тут на стеке выделено 4 байта
        let mut idx: i32 = 10;
        // Указатель на эти 4 байта
        let ptr = &mut idx as *mut i32;
    
        // По сути указатель на те же 4 байта, но на деле указатель на 40 байт
        let buffer = unsafe { core::slice::from_raw_parts_mut(ptr, 10) };
    
        // И тут мы работаем с этими 40 байтами,
        // но нормальные из них только 4 байта в начале,
        // а остальные смотрят в глубь стека и меняют его
        // то есть перетирают адрес возврата и данные на стеке вызвавшей функции
        for pixel in buffer.iter_mut() {
            *pixel = 0x7d2b7500;
        }
    }
    То есть после завершения данная функция должна не вернуться в вызывающую функцию, а передать управление непонятно куда.

    Ну и еще один момент, это оптимизация, данная функция не имеет сайд эффектов и ничего не возвращает. После инлайна функции core::slice::from_raw_parts_mut и метода Iterator::iter_mut компилятор вполне может понять, что данный код не имеет сайд эффектов, а значит бесполезен и его можно вырезать. Вот собственно он его и вырезает.
    Во втором же примере у нас есть сайд эффект - изменение данных по указателю пришедшему извне через аргументы.

    P.S. Во втором примере кстати тоже можно увидеть работу оптимизации, цикл из 10 итераций, пишущих i32 в смещение по указателю, был заменен на обычные 5 записей i64 в смещение по указателю.
    Ответ написан
    1 комментарий
  • Какой тип данных у переменной a?

    bingo347
    @bingo347
    Crazy on performance...
    У переменной a указан тип встроенный массив [T; n]. Но у такого типа нет метода get_unchecked_mut(). Он есть у среза на массив [T]
    Тут нужно понять как в Rust работает оператор точка.

    Во-первых, мало кто пишет, но операторов точка на самом деле в Rust целых 2:
    - Доступ к полю структуры/кортежа/юниона
    - Вызов метода
    Rust различает их по наличию круглых скобок:
    struct S {
        a: i32,
        f: fn(),
    }
    
    impl S {
        fn a(&self) {}
    }
    
    let s = S { a: 0, f: || {} };
    s.a; // Доступ к полю
    s.a(); // Вызов метода
    (s.f)(); // Вызов функции по указателю, который лежит в поле f

    С доступом к полю все просто, компилятор преобразует его в смещение в памяти где это поле лежит относительно самой структуры.
    А вот с вызовом метода все интереснее, Rust пытается рассахарить его в одну из следующих конструкций в следующем порядке:
    S::a(s);
    S::a(&s);
    S::a(&mut s);
    <S as Deref>::Target::a(S::deref(&s));
    <S as DerefMut>::Target::a(S::deref_mut(&mut s));
    <<S as Deref>::Target as Deref>::Target::a(<S as Deref>::Target::deref(S::deref(&s)));
    <<S as DerefMut>::Target as DerefMut>::Target::a(<S as DerefMut>::Target::deref_mut(S::deref_mut(&mut s)));
    // ...
    и так пока либо не найдет вариант, который компилируется, либо пока не обнаружит что для очередного типа не реализован трейт Deref.
    Подробнее можно почитать тут: https://doc.rust-lang.org/stable/nomicon/dot-opera...

    У всех массивов есть Deref к слайсу, в core библиотеке прописано что-то вроде:
    impl<T, const N: usize> Deref for [T; N] {
        type Target = [T];
        fn deref(&self) -> &[T] {
            // ...
        }
    }
    За счет этого все массивы (а так же векторы, у которых тоже Deref к слайсу) получают методы слайса. И по тому же принципу String получает методы str.

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

    Ну и из комментов к вопросу:
    Что тут делает unsafe код как раз понятно
    Однозначно понятно, он здесь делает UB так как обращается к памяти владелец которой неизвестен.
    Ответ написан
    1 комментарий
  • Как подключить и использовать json конфиг в rust?

    bingo347
    @bingo347
    Crazy on performance...
    В константах как не странно можно использовать только константы.
    Варианта 2:
    1. Написать процедурный макрос, который прочитает нужный JSON в компайл-тайм и сгенерирует код константы, так кстати и саму структуру можно генерировать.
    2. Зашить байты JSON в бинарь через макрос include_bytes!, а парсить при старте программы или в lazy_static.

    Но вообще у меня есть ощущение, что Вы в принципе неправильно решаете свою задачу. Константы - это по своей сути компайл-тайм сущности, а смысл конфига обычно - получить параметры в ран-тайм.
    Скорее всего Вам достаточно будет простого lazy_static
    Ответ написан
    5 комментариев
  • Как итерировать несколько объектов в одном цикле RUST?

    bingo347
    @bingo347
    Crazy on performance...
    let a = [1, 2];
    let b = [3, 4];
    for (a, b) in a.into_iter().zip(b.into_iter()) {
        println!("{a}, {b}");
    }

    Метод zip есть у любого объекта реализующего трэйт итератора
    https://doc.rust-lang.org/std/iter/trait.Iterator....
    Ответ написан
    Комментировать
  • Как использовать winapi в Rust?

    bingo347
    @bingo347
    Crazy on performance...
    Ответ написан
    Комментировать
  • Существуют ли фреймворки, позволяющие писать бекенд на rust+javascript?

    bingo347
    @bingo347
    Crazy on performance...
    Смотря что Вы понимаете под rust+javascript
    Если это проект на rust со вставками на js (например для ssr), то есть несколько вариантов:
    1. Рядом работают приложение на rust и приложение на node.js, общаются по tcp/udp/unixsock/etc.
    2. Интегрировать rust приложение как нативный аддон к node.js, общение через ffi, для этого есть neon.
    3. Внедрить js движок в приложение на rust, например есть байндинг к v8 из deno или гораздо более легковесный quick-js, правда байндинг пока сильно урезанный и не факт, что станет лучше, занимается им явно человек далекий от js. Я в свободное время пилю свою safe обертку для данного движка, моя цель как раз таки запуск ssr js фреймворков в rust приложениях, но что-то рабочее будет думаю не раньше октября.
    Ответ написан
    Комментировать
  • С чего начать изучение языка?

    bingo347
    @bingo347
    Crazy on performance...
    Вы нигде и никогда не найдете руководства по переходу с языка А на язык Б. Бэкграунд у всех разный и такие руководства просто не имеют смысла.

    А еще ответьте себе на вопрос, зачем Вам Rust? В JS/TS нет и половины тех проблем, которые он решает. Да, ценой некоторых ресурсов, но нет.
    А еще, есть два типа людей - те, которые думают, что в TypeScript/Java/C# сильная система типов, и те, кто понимает, что они в разы слабее чем в ML языках. Система типов Rust - это доработанная система типов OCaml со всеми вытекающими. Тут кстати можно в мире JS попробовать ReasonML/ReScript и сравнить с TypeScript, чтоб понять суть было проще.

    А общий рецепт освоения Rust выглядит так:
    1. Прочитать Rustbook на русском, но лучше на английском.
    2. Посмотреть лекции Алексея Кладова
    3. Rust by example на русском или на английском
    Ну и конечно никуда без практики

    И да, всю прелесть borrow checker понять не получится, пока не по управляете памятью руками. Но для этого не обязательно лезть в C/C++, можно на том же Rust, правда придется окунутся в unsafe, так что отложите это на попозже, а сейчас воспримите borrow checker, как часть строгой системы типов, которая всеми силами пытается Вам помочь.
    Ответ написан
  • Как исправить ошибку used of moved value?

    bingo347
    @bingo347
    Crazy on performance...
    temp = temp.into_iter().rev().collect();
    (0..cols).for_each(|col| vec[row][col] = temp[col]);

    temp владеет некоторой памятью
    (0..cols).for_eachитерирует и вызывает переданную ему функцию cols раз
    замыкание преданное в for_each захватывает vec по &mut ссылке, а temp по значению
    на первой итерации temp[0] делает move в vec[row][0], так как в memory model Rust ни один тип не может существовать в виде "огрызка с дыркой", компилятор дописывает drop(temp) в конец функции |col| vec[row][col] = temp[col], а так как temp захвачен замыканием по значению (по сути он является частью замыкания), то в конце итерации цикла внутри for_each еще и само замыкание дропается.
    Другими словами for_each хочет от вас FnMut, а Вы ему дали только FnOnce.

    Думаю Вам поможет одна из этих функций:
    https://doc.rust-lang.org/beta/std/mem/fn.take.html
    https://doc.rust-lang.org/beta/std/mem/fn.replace.html

    index out of bounds: the len is 2 but the index is 2
    Это не ошибка компиляции, а runtime panic, которая говорит о там, что Вы вышли за границу слайса

    P.S. я надеюсь у Вас усть понимание, что здесь O(n3)?
    while (0..rows).map(|r| vec[r].first().unwrap()).all(|&x| x == 0) {
        (0..rows).for_each(|r| {vec[r].remove(0);});
    };
    Ответ написан
    Комментировать
  • Хайп вокруг ЯП Rust и C?

    bingo347
    @bingo347
    Crazy on performance...
    Насколько критичной проблемой для программиста является ручное управление памятью, которое называют недостатком языка Си?
    Неосвобожденная память (утечка памяти) - это самое безобидное, что может произойти.
    - Сделать free дважды - это UB
    - Забыли проверить результат malloc/calloc/realloc на не NULL, разыменовали - UB
    - Почитали память, в которую ни разу не писали - UB
    - Разыменовали указатель после free - UB
    - Гонка данных - UB
    - ...и еще дофига всего, от чего в лучшем случае программа просто будет работать неправильно, например спалит Ваш пароль, или переведет Ваши деньги на другой счет 10 раз.

    Новый язык программирования Раст, как заявляют, лишен этого недостатка

    Система типов Rust гарантирует лишь одно - в safe коде не будет UB. Утечка памяти, кстати, не является UB, поэтому память вполне себе можно утечь в safe коде, помимо циклических счетчиков ссылок std дает немало возможностей сделать это напрямую:
    https://doc.rust-lang.org/beta/std/mem/fn.forget.html
    https://doc.rust-lang.org/beta/std/mem/struct.Manu...
    https://doc.rust-lang.org/beta/std/boxed/struct.Bo...
    https://doc.rust-lang.org/beta/std/vec/struct.Vec....

    но разве число ошибок в программе зависит именно от наличия или отсутствия ручного управления памятью
    В Rust ручное управление памятью, как и в C и в C++, просто есть культура, что если некая структура аллоцировала память, то она ее освободит. Всякие Vec, Box и т.д. делают это в Drop. В C++ многие повседневные типы так же освобождают в деструкторе память, которую они выделили. Однако в Rust есть разделение на safe и unsafe код и для прикладного программирования safe возможностей более чем достаточно. В C++ же весь код unsafe.

    разве общее число ошибок не перераспределяется на другие недостатки программы
    Нет, не перераспределяется. Хорошая система типов действительно может избавить от многих ошибок, что в целом сделает ПО более надежным. Но надо понимать, что от всех ошибок не избавит ни что. Банальная дискоммуникация с заказчиком порождает огромное число багов.
    Но в Rust очень быстро привыкаешь к такому замечательному подходу в разработке, как Type Driven Development, который позволяет предупредить многие ошибки еще до написания кода. После Rust я стал применять этот подход и в других ЯП, и он работает очень хорошо, даже там, где типизация не такая строгая.

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

    P.S. Вот интересная статья про Rust к прочтению: https://steveklabnik.com/writing/a-sad-day-for-rust
    И к чему она привела: https://github.com/fafhrd91/actix-web-postmortem
    Ответ написан
    4 комментария
  • Как стильно переписать подобный код?

    bingo347
    @bingo347
    Crazy on performance...
    Я бы как то так сделал:
    macro_rules! low_high_wait {
        (low) => {
            led.set_low().unwrap();
        };
        (high) => {
            led.set_high().unwrap();
        };
        (wait) => {
            block!(timer.wait()).unwrap();
        };
        (low, $($rest:tt),+) => {
            low_high_wait!(low);
            low_high_wait!($($rest),+);
        };
        (high, $($rest:tt),+) => {
            low_high_wait!(high);
            low_high_wait!($($rest),+);
        };
        (wait, $($rest:tt),+) => {
            low_high_wait!(wait);
            low_high_wait!($($rest),+);
        };
    }
    for _ in 0..24 {
        low_high_wait!(wait, high, low, low, wait);
    }
    for _ in 0..24 {
        low_high_wait!(wait, high, wait, low);
    }
    for _ in 0..24 {
        low_high_wait!(wait, high, low, low, wait);
    }
    for _ in 0..24 {
        low_high_wait!(wait, high, wait, low);
    }
    Ответ написан
    Комментировать
  • Rust сырой в ближ. время?

    bingo347
    @bingo347
    Crazy on performance...
    Подскажите пожалуйста какова на данный момент ситуация с Rust
    Дефицит вакансий, дефицит грамотных разработчиков. Те что есть хотят много денег.
    до сих пор еще дорабатывают, вышел ли в продакшен?
    он "вышел в продакшен" 5-6 лет назад, а насчет доработок - этот процес вечен, ну или пока язык не забросили, ну или как с Go не уперлись в собственный говнокод
    Может ли на данный момент потягаться по кол-ву библиотек в web с Golang и в машинном обучении с Python?
    Ну так посмотрите сами: https://crates.io/
    Планируется ли что-то в ближ. год/два?
    https://blog.rust-lang.org/2020/09/03/Planning-202...
    Ответ написан
    Комментировать
  • Почему может падать программа с exit code: 0xc0000005, STATUS_ACCESS_VIOLATION?

    bingo347
    @bingo347
    Crazy on performance...
    Для начала стоит переписать все без unsafe (он здесь совсем не нужен)
    Вместо некорректного шаринга уникальных ссылок IS_FILE_READ используйте AtomicBool
    С CHAR_BUFFER тоже нарушили правило либо 1 mut/uniq borrow либо много shared borrow, заверните в Mutex, а лучше, учитывая однонаправленность данных передавайте через channel
    Ответ написан
  • Как разобрать HTTP запрос?

    bingo347
    @bingo347
    Crazy on performance...
    Что делать если размер запроса больше чем размер буфера?
    Читать в цикле. read вообще не гарантирует, что заполнит буфер полностью, но больше его размера он точно за раз не прочитает. А еще он возвращает io::Result<usize>, в котором сообщает, сколько реально байт было прочитано.
    1.1) Нужно будет читать пока не найдется CRLF в буфере ?
    Пока read не вернет Ok(0), ну или ошибку. Хотя с ошибкой не все так однозначно, согласно доке может вылететь Err(io::ErrorKind::Interrupted) при котором стоит повторить попытку чтения. Вообще CRLF будет после каждого заголовка, а когда заголовки закончатся будет 2 CRLF подряд, а потом еще может быть тело запроса, а может и не быть.
    1.2) для этого мне нужно пройтись по буферу и искать CRLF . если его нет то очистить буфер и продолжать читать ?
    нет, нужно распарсить то что пришло, куда-то сохранить, а потом продолжить чтение.
    2) Как отделять один http запрос от другого ?
    Если у нас не keep-alive, то каждый запрос будет в отдельном соединении, но keep-alive наступает только если обе стороны прислали заголовок Connection: keep-alive Можете сделать по простому, и отвечать с заголовком Connection: close, все равно в учебном проекте производительность у Вас будет никакая. Но если хотите все же заморочиться, то правило тоже не сложное - следующий запрос начинается в следующем же байте, где закончился текущий. Размер тела запроса в байтах можно узнать из заголовка Content-Length, а если его нет, то можете считать, что его значение 0.

    Что следует почитать для Вашей задумки:
    1. Спеку http
    2. Исходники hyper
    3. Исходники actix-web
    Ответ написан
    Комментировать
  • Какие задачи решают на Rust, а какие на Golang?

    bingo347
    @bingo347
    Crazy on performance...
    Какие задачи решают на Rust
    любые. Rust - язык общего назначения, применимый к большинству возможных задач. Rust достаточно высокоуровневый для написания на нем прикладного ПО и компилируется в достаточно эффективный машинный код, для применения в ядрах ОС, драйверах или embedded разработке. Так же Rust на сегодня имеет самый маленький размер при компиляции в wasm, что критично для использования в web. Я честно не знаю такой сферы, к которой бы не подошел Rust.
    Единственной проблемой в применимости Rust я вижу недостаточную его распиаренность в РФ, что часто бывает самым важным критерием для "манагеров" и прочих людей принимающих решения о используемом стеке.
    а какие на Golang
    Golang тоже язык общего назначения, но имеющий ряд ограничений:
    - Крайне тяжелый рантайм не дает возможность использовать его в wasm, embedded или компонентах ядра.
    - Необходимость в сборке мусора опять таки ограничивает разработку для embedded или компонентов ядра.
    - Отказ от llvm в качестве бэкенда компилятора ограничивает число целевых платформ.
    Можно один заменить другим?
    Rust спокойно заменяет Golang в любой возможной на последнем задаче, наоборот же иногда имеем ряд ограничений.

    Вместо P.S.:
    Golang скорее всего окажется более быстрым для прототипирования и быстрого старта. Однако отсутствие полиморфизма в любом виде (утиная типизация не в счет) и ограниченность одной парадигмой структурного программирования делает этот язык крайне дорогим в поддержке. Так же этому (и быстрому прототипированию и дорогой поддержке кода) способствует лютая ненависть создателей языка к принципу DRY.
    Rust имеет такую редкую сегодня строгую типизацию, одним из нюансов которой являются концепции владения и заимствования (которые позволяют делать автоматическое управление памятью в compile time), что порождает с одной стороны высокий порог входа в технологию (что сглаживается человекопонятным выводом компилятора, если входящие умеют читать, что еще более редко встречается сегодня, чем строгая типизация), но так же удешевляет поддержку продукта длительное время. Так же надо понимать, что Rust не спасет от кривых рученок быдлокодеров (разве что они не смогут его освоить), так как при большом желании можно сделать и утечки памяти и дедлоки и гонки данных (хотя в Golang это все сделать на порядок проще).
    Ну и надо не забывать, что много где присутствует hype-driven-development и Golang распиарен, а Rust нет.
    Ответ написан
    3 комментария
  • Как ограничить тип T примитивными типами f32 и f64?

    bingo347
    @bingo347
    Crazy on performance...
    Ограничить дженерик-тип можно только типажами (traits).
    Что Вы хотите сделать? Какие методы f32 и f64 Вам нужны?
    Возможно данные методы уже реализованы в типажах стандартной библиотеки - тогда нужно ограничивать их суммой.

    Другой вариант, сделать свой типаж, ограничить дженерик тип им, имплементировать этот типаж для f32 и f64
    Имплементация должна представлять обертки над нужными действиями

    UPD: если нужно ограничение дженерик, на использование метода exp у f32 и f64:
    Типаж-контракт на метод exp:
    trait Exp {
        fn exp(self) -> Self;
    }
    
    impl Exp for f32 {
        fn exp(self) -> Self {
            <f32>::exp(self)
        }
    }
    
    impl Exp for f64 {
        fn exp(self) -> Self {
            <f64>::exp(self)
        }
    }


    так же можно воспользоваться следующим крэйтом:
    https://crates.io/crates/num
    Ответ написан
    2 комментария
  • Что такое mod в Rust?

    bingo347
    @bingo347
    Crazy on performance...
    mod - это объявление дочернего модуля/пространства имен
    Есть 2 способа объявления - через блок и через отдельный файл:
    // через блок
    mod some_module {
      // тут изолированное пространство имен
      pub fn hello() {
        println!("Hello world");
      }
    }
    fn main() {
      some_module::hello(); // Hello world
    }
    
    // подключаем модуль из файла ./some_other_module/mod.rs или ./some_other_module.rs
    mod some_other_module;

    так же через оператор pub можно делать реэкспорт модуля целиком, делая тем самым структуру пространств имен Вашего крэйта
    Ответ написан
    Комментировать
  • Почему выходит паника - panicked at 'panicked at 'index out of bounds: the len is 0 but the index is 0'?

    bingo347
    @bingo347
    Crazy on performance...
    У вас вектор matrix_c изначально пустой, Вы пытаетесь писать в несуществующий элемент.
    Вижу, что у Вас закоменчен вариант с методом push (почему он Вам не подошел не знаю).
    Как вариант можно сразу создать вектор нужного размера, заполненный значениями по умолчанию (например нулями):
    // добавлю пару констант, для простоты модификации
    let rows = 2;
    let cols = 2;
    
    let matrix_a =  vec![1,2,3,4];
    let matrix_b =  vec![5,6,7,8];
    let mut  matrix_c = vec![0; rows * cols]; // 0 - default, rows * cols - размер (4)
    
    for row in 0..rows {
      for col in 0..cols {
        let mut vec_buf = 0;
        for k in 0..2 {
          vec_buf = vec_buf + matrix_a[row * 2 + k] * matrix_b[k * 2 + col];
        }
        matrix_c[row * 2 + col] = vec_buf; // теперь должно быть все ок
                             // так как спокойно передаем владение существующему элементу вектора
      }
    }
    
    
    for (i, x) in matrix_c.iter().enumerate(){
      println!(" элем = {:?}, значение = {:?}", i, x);
    }
    Ответ написан
    1 комментарий