Ответы пользователя по тегу Rust
  • Зачем нужен метод clone() если есть ссылки?

    bingo347
    @bingo347
    Crazy on performance...
    Метод clone из трейта Clone производит копию некоторого ресурса, мы получаем еще один ресурс во владение. При этом данные не обязательно копируются, логика может быть любой, например Rc и Arc в нем просто увеличивают счетчик ссылок, а ресурс остается одним, разделяя владение на несколько владельцев.
    Ссылки ограничены временем жизни ресурса, на который они ссылаются, к тому же менять ресурс мы можем только по уникальной ссылке, а метод clone создаст копию ресурса и отдаст владение этой копией, что позволит этой копии жить дольше, чем исходный ресурс, а так же изменять эту копию независимо от исходного ресурса.
    Ответ написан
    1 комментарий
  • Как изменить данные завернутые в Box, который завернут в Option?

    bingo347
    @bingo347
    Crazy on performance...
    Во-первых стоит познакомится с такой конструкцией, как if let.
    Но здесь, с Box, у Вас не получится обойти borrow checker.
    Нормальным решением будет использовать счетчики ссылок (Rc или Arc) и избавиться с их помощи от зависимости на лайфтаймы. А для ссылки на parent нужно использовать Weak ссылку, чтоб она не влияла на подсчет ссылок.

    https://doc.rust-lang.org/std/rc/struct.Rc.html
    https://doc.rust-lang.org/std/rc/struct.Weak.html

    https://doc.rust-lang.org/std/sync/struct.Arc.html
    https://doc.rust-lang.org/std/sync/struct.Weak.html

    use std::rc::{Rc, Weak};
    
    pub struct Node {
        parent: Weak<Node>,
        data: u32,
        pub left: Option<Rc<Node>>,
        pub right: Option<Rc<Node>>,
    }
    
    #[derive(Debug)]
    pub struct SetChildError;
    
    impl Node {
        pub fn new(data: u32) -> Self {
            Self {
                parent: Weak::new(),
                data,
                left: None,
                right: None,
            }
        }
    
        pub fn set_left(self: &mut Rc<Self>, mut child: Rc<Self>) -> Result<(), SetChildError> {
            let child_mut = Rc::get_mut(&mut child).ok_or(SetChildError)?;
            child_mut.parent = Rc::downgrade(self);
    
            let self_mut = Rc::get_mut(self).ok_or(SetChildError)?;
            self_mut.left = Some(child);
    
            Ok(())
        }
    }
    Ответ написан
    5 комментариев
  • Правильно ли решил задачу?

    bingo347
    @bingo347
    Crazy on performance...
    Задачка прикольная для обучения, позволяет поработать сразу со многими конструкциями языка.

    Для начала комментарии по Вашему решению:
    1. Тип String алоцирует память на куче, операция += над типом String может приводить к реалокации памяти. Алокация памяти не дешевая операция, в данной задаче можно обойтись вообще без нее.
    2. Для повторяющихся операций придумали циклы, а с циклами хорошо сочетаются коллекции, например массивы и слайсы.
    3. Уже писали в комментах, но все же, if true - бесполезная штука.

    Теперь давайте посмотрим на текст песни. У нас 12 куплетов.
    Каждый куплет начинается с очень похожих строчек: "On the first day of Christmas", "On the second day of Christmas", и т.д., меняется только числительное.
    Потом всегда идет строчка "My true love sent to me" в каждом куплете.
    Затем идет от 1 до 12 строчек, повторяющиеся из куплета в куплет, но в i-том куплете будет только i таких строчек. Притом тут есть особенность, что первая строчка в куплете иногда отличается от своих аналогов в последующих куплетах.
    Ну и наконец, в 12 куплете мы видим, что строка "And a partridge in a pear tree" присутствует 2 раза, что выбивается из общего правила.

    Решение с объяснением

    Для начала вынесем факт того, что у нас 12 куплетов в константу, это нам пригодится для дальнейшего объявления массивов и итерации по ним.
    const NUMBER_OF_VERSES: usize = 12;

    Заведем массив числительных, которые меняются в первых строчках куплетов.
    const NUMERALS: [&str; NUMBER_OF_VERSES] = [
        "first", "second", "third", "fourth", "fifth", "sixth", "seventh", "eighth", "ninth", "tenth",
        "11th", "12th",
    ];


    Так же заведем массив, для повторяющихся строчек куплетов.
    const LINES: [&str; NUMBER_OF_VERSES] = [
        "And a partridge in a pear tree",
        "Two turtle-doves",
        "Three French hens",
        "Four calling birds",
        "Five golden rings (five golden rings)",
        "Six geese a-laying",
        "Seven swans a-swimming",
        "Eight maids a-milking",
        "Nine ladies dancing",
        "Ten lords a-leaping",
        "Eleven pipers piping",
        "12 drummers drumming",
    ];


    Так же нам понадобится массив первых строчек куплетов, так как среди них есть отличающиеся. Я нашел такие в 1 и 11 куплете (так как массивы у нас индексируются с 0, то они будут под индексами 0 и 10 соответственно). Остальные же будут такими же как в массиве LINES, а значит можно по экономить размер бинаря и занимаемую память за счет того что в массивах у нас только ссылки на строки, которые можно копировать.
    Тут конечно можно написать что-то вроде
    const DIFFERING_LINES: [&str; NUMBER_OF_VERSES] = [
        "A partridge in a pear tree",
        LINES[1],
        LINES[2],
        // ...
    ];
    но это дикая копипаста, которая плохо читается и подвержена ошибкам.
    Благо в Rust есть константные функции, которые могут выполняться в compile-time и возвращают константы, а значит можно наш константный массив сгенерировать. Правда константные функции довольно сильно ограничены, в них можно пользоваться лишь ветвлениями, циклами, простейшей арифметикой (в том числе над указателями, а значит и получать доступ к элементам массива по индексу). Так же можно вызывать другие константные функции. Цикл for нам к сожалению тут не доступен, так как он работает поверх итераторов, а методы IntoIter::into_iter и Iterator::next, которые он вызывает, не являются константными. Но цикл со счетчиком можно сделать и через while. По итогу получим такую функцию:
    const fn gen_differing_lines() -> [&'static str; NUMBER_OF_VERSES] {
        let mut i = 0;
        let mut lines = [""; NUMBER_OF_VERSES];
        while i < NUMBER_OF_VERSES {
            lines[i] = match i {
                0 => "A partridge in a pear tree",
                10 => "I sent 11 pipers piping",
                i => LINES[i],
            };
            i += 1;
        }
        lines
    }

    И инициализируем ей наш массив:
    const DIFFERING_LINES: [&str; NUMBER_OF_VERSES] = gen_differing_lines();


    Теперь еще особенность, в самих куплетах, строчки из LINES в них идут в обратном порядке. То есть для 4 куплета (индекс 3) нам помимо строчки DIFFERING_LINES[3] нам нужно напечатать строки под индексами 2, 1 и 0 из LINES. Для удобства вынесем печать строк из LINES в отдельную функцию:
    fn print_verse(mut i: usize) {
        while i > 0 {
            i -= 1;
            println!("{}", LINES[i]);
        }
    }


    Ну и осталось написать основной код для печати:
    fn main() {
        for i in 0..NUMBER_OF_VERSES {
            println!("On the {} day of Christmas", NUMERALS[i]);
            println!("My true love sent to me");
            println!("{}", DIFFERING_LINES[i]);
            print_verse(i);
        }
        println!("{}", LINES[0]);
    }


    А полное решение можно посмотреть и запустить тут:
    https://play.rust-lang.org/?version=stable&mode=re...
    Ответ написан
    Комментировать
  • Почему 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 комментария