• Слайс на неинициализированную память?

    bingo347
    @bingo347
    Crazy on performance...
    MaybeUninit под капотом - это union, то есть компилятор ничего не знает, инициализированное там значение или нет и перекладывает эту ответственность на программиста. А значит UB здесь не будет.
    То есть спокойно делаете &mut [MaybeUninit<*mut State>], инициализируете элементы и после std::mem::transmute в &mut [*mut State]
    Ответ написан
    2 комментария
  • Как идентифицировать target в build.rs?

    TrueBers
    @TrueBers
    Гуглю за еду
    А как оно будет работать, если build.exe для того и предназначен, чтобы запускаться самим тулчейном на хостовой системе, а не на таргете?

    Вот список переменных окружения, которые получаются через std::env::var в build.rs. Для target-triple тебе нужен TARGET.

    Чтобы получить host-triple, нужна переменная HOST.
    Ответ написан
    Комментировать
  • FnMut в немутабельной переменной?

    bingo347
    @bingo347
    Crazy on performance...
    Мутабельность нужна для вызова, так как FnMut вызывается по мутабельной ссылке.
    Здесь же только передаётся владение в метод run

    P.S. что-то мне подсказывает, что сигнатура не совсем корректная. Отсутствует объявление дженерика T (и возможно его ограничений), отсутствует лайфтайм для ссылки в колбэке:
    pub fn run<T, F>(self, event_handler: F) -> Result<(), EventLoopError>
        where
            F: for<'a> FnMut(Event<T>, &'a EventLoopWindowTarget<T>),
    {
        self.event_loop.run(event_handler)
    }
    Ответ написан
    Комментировать
  • FnMut в немутабельной переменной?

    vabka
    @vabka Куратор тега Rust
    Потому что замыкание, которое не имеет мутабельных ссылок на окружение - автоматически реализует трейты Fn, FnMut и FnOnce.

    В функции run указан самый широкий вариант из возможных (Fn обычный был бы слишком узким, а FnOnce для run не подходит, тк замыкание предполагается вызывать много раз)

    https://habr.com/ru/articles/588917/
    https://stackoverflow.com/questions/30177395/when-...
    Ответ написан
    Комментировать
  • Как раздельно инициализировать структуру?

    vabka
    @vabka Куратор тега Rust
    Тебе ссылка через & не подойдёт тут.
    Используй Arc и Weak.
    Либо делай window тоже как ссылку на окно, которое создано в main.
    Ответ написан
    3 комментария
  • Каких вещей следует избегать в Rust?

    bingo347
    @bingo347
    Crazy on performance...
    Я знаю, что следует избегать всяких "продвинутых" штук из ряда связных списков, самореферентных структур и т.п.
    Односвязные списки никаких проблем не доставляют (ну кроме того, что они плохо ложатся на процессорный кэш). Для двусвязных списков и самореферентных структур придётся использовать сырые указатели и unsafe.

    Ещё я обнаружил, что создание больших структур, с методами, с кучей полей, обычно приводит к проблемам с borrow checker.
    Borrow checker абсолютно плевать на размер структур. Это никак не связано.

    А если в структуре будет ссылка или иное заимствование, то это гарантированные проблемы.
    Нет ни каких проблем.

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

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

    Так, наличие в умеренных размерах программе, которая по сути была одной функцией, лишь одной структуры хранящей ссылку, поставило крест на попытке структуризации программы в более человеческий вид.
    Что-то делаете не так. Без конкретных примеров кода сказать сложно.

    И очень часто в Rust программах, мне приходится идти на более уродливую архитектуру, дабы избежать проблем с (почти ненужным в однопоточном коде) borrow checker.
    Что-то делаете не так. Скорее всего просто не понимаете borrow checker и пытаетесь писать на новом языке так, как привыкли в каком-то другом.

    И в вопросе о borrow checker, разве не является тот факт, что большинство библиотек избегает &mut self в изменяющих что-то методах, звоночком к наличию большим проблем в языке?
    О каком большинстве речь? Библиотеки используют мутабельные ссылки там где это нужно. Если метод действительно что-то меняет, то будет мутабельная ссылка ну и иногда будет использоваться interior mutability там где это необходимо. В языке нет проблем с мутабельными ссылками.

    В общем, посоветуйте что-то что-бы помогало меньше бороться с borrow checker, потому что сейчас я очень много времени трачу именно на это.
    Для начала понять его. Понять какую проблему он решает. Почитайте, что такое undefined behavior. Почитайте, что такое алиасинг.

    Возможно где-то альтернативой мутабельным ссылкам будут Cell/RefCell в однопоточном коде и Mutex/RwLock в многопоточном.
    Возможно если покажете примеры кода, где у Вас проблемы, то можно будет подсказать что-то более конкретное.
    Ответ написан
    3 комментария
  • Плохо решаю задачи, как повысить квалификацию?

    wataru
    @wataru
    Разработчик на С++, экс-олимпиадник.
    Единственный способ научиться решать задачи - решать задачи. Много и разных. Если задачи по програмированию, то прорешивайте какой-нибудь leetcode. Если минут за 5 вообще идей нет - смотрите подсказки. Еще через 10 - смотрите чужие решения. Важно только потом все равно эти решения самостоятельно написать потом сразу.
    Ответ написан
    Комментировать
  • Вызов функций С++ из .h?

    bingo347
    @bingo347
    Crazy on performance...
    Скорее всего придётся сделать extern C обёртку над библиотекой и биндится уже с ней. Ну и надо понимать, что никакие абстракции плюсов вроде темплейтов, классов и т.д. в раст не протащить.

    В какой-то степени может помочь cxx, но надо понимать, что эта штука тоже не всесильна.
    Ответ написан
  • Вызов drop в вызове drop?

    bingo347
    @bingo347
    Crazy on performance...
    Почему drop принимает ссылку, а не значение?

    Потому что метод drop трейта Drop вызывается компилятором, каждый раз когда переменная владеющая чем-либо выходит из области видимости. И сам метод drop тут не исключение. То есть если бы self тут был по значению, компилятор был бы обязан его дропнуть в конце функции, что вызвало бы бесконечную рекурсию.

    Причём если вызывать drop() руками
    Функция core::mem::drop никакого отношения к трейту Drop не имеет. Если Вы глянете на её реализацию, то это просто пустая функция, которая принимает аргумент по значению, а он уже дропается на общих основаниях, так как выходит из области видимости в ней.

    Почему сначала вызывается drop для A, а потом для B? По логике drop должен сначала вызываться для полей.
    У Вас неверная логика. В метод трейта Drop приходит ссылка, а значит должна быть гарантия того что данные по ней полностью валидные. Всегда дропается сначала внешняя структура,а затем её поля. Более того компилятор не даст Вам даже мувнуть части структуры имплиментирующей Drop.

    Если очень нужно, то владение из поля можно забрать через std::mem::swap/std::mem::replace/std::mem::take
    Хотя проще это сделать обернув такое поле в Option и забирая владение его методом take
    Ответ написан
    2 комментария
  • Правильно ли завершается TCP соединение с браузером?

    15432
    @15432
    Системный программист ^_^
    Вплоть до 29 пакета всё кажется идёт хорошо, но в 29 пакете браузер пытается отправить ACK, как я понял в ответ на FIN ACK, но похоже тут что-то не так.

    всё ок, браузер почему-то отправил сразу два ACK, без учета FIN (29) и с учетом FIN (30). Ну лишний пакет получился, и ладно. Последовательность FIN/ACK завершена корректно.

    У меня подозрение, что в после того как сервер в 28 пакете отправил FIN ACK он не дожидается ACK от клиента, верно ли это?

    не, там всё нормально

    но браузер тут-же создаёт новое подключение

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

    не присылает никаких запросов в этом подключении

    видимо в index.html не было больше ничего, что можно запросить

    поскольку я хочу просто отправить ответ и закрыть соединение

    так и вышло, но в первом соединении. второе так и висело на всякий случай.

    в этом варианте клиенту с сервером чуть-чуть не хватило что-бы полностью обработать последовательность:

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

    Правильно ли я понимаю, что сервер посылая FIN ACK не дожидается ACK и в целом неверно производит завершение TCP подключения, завершая соединение раньше времени?

    всё корректно дожидается, тут проблем нет

    Именно поэтому браузер инициирует повторное подключение, но не присылает никаких данных.

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

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

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

    (я пробовал разные браузеры)

    видимо все браузеры конкурируют по скорости работы с Web и делают такую оптимизацию
    Ответ написан
    3 комментария
  • Возврат собственного типа ошибки из main с печатью сообщения об ошибке?

    bingo347
    @bingo347
    Crazy on performance...
    fn main() -> MainResultWrapper {
        MainResultWrapper(main_inner())
    }
    
    #[inline(always)]
    fn main_inner() -> Result<(), GlobalError> {
        let config = read_config_from_file()?;
        Ok(())
    }
    Ответ написан
    Комментировать
  • Элегантный способ парсинга и обработки ошибок?

    @0x0f80
    Например метод map_err. Статейка
    pub fn build_server_config(&self) -> Result<Config, ConfigError> {
        let socket_addr_v4 = self.socket_addr.parse::<SocketAddrV4>()
            .map_err(ConfigError::WrongSocketAddr)?;
        Ok(Config {
            // инициализация полей Config
        })
    }
    Ответ написан
    1 комментарий
  • Где умные указатели размещают полученные данные?

    vabka
    @vabka Куратор тега Rust
    Cell<T> has the same memory layout and caveats as UnsafeCell<T>. In particular, this means that Cell<T>has the same in-memory representation as its inner type T.


    У остальных нет никаких обязательств о memory representation, но они тоже ничего в куче не выделяют.

    Ну и всегда можно посмотреть на исходники)
    https://doc.rust-lang.org/src/core/cell.rs.html#293
    https://doc.rust-lang.org/src/core/cell.rs.html#2034
    https://doc.rust-lang.org/src/core/cell.rs.html#700
    https://doc.rust-lang.org/src/core/cell/once.rs.html#33
    Ответ написан
    Комментировать
  • Как работает этот код?

    bingo347
    @bingo347
    Crazy on performance...
    Очень упрощенно HashMap можно представить следующим образом:
    pub struct HashMap<K, V> {
        table: Table<(K, V)>,
    }
    
    struct Table<T> {
        // битовая маска занятых ячеек в items
        mask: u64,
        items: Box<[std::mem::MaybeUninit<Item<T>>; 64]>,
        len: usize,
    }
    
    struct Item<T> {
        data: T,
        next: Option<std::ptr::NonNull<Item<T>>>,
    }


    А Entry так:
    pub enum Entry<'a, K, V> {
        Vacant(VacantEntry<'a, K, V>),
        Occupied(OccupiedEntry<'a, K, V>),
    }
    
    pub struct VacantEntry<'a, K, V> {
        hash: u64,
        key: K,
        table: &'a mut Table<(K, V)>,
    }
    
    pub struct OccupiedEntry<'a, K, V> {
        elem: Bucket<(K, V)>,
        table: &'a mut Table<(K, V)>,
    }
    
    // указатель на Item.data
    struct Bucket<T> {
        ptr: std::ptr::NonNull<T>,
    }


    Как можно заметить у Entry есть лайфтайм, который связывает его с HashMap от которой он создан. А внутри есть мутабельная ссылка с этим лайфтаймом на таблицу с данными HashMap.
    Метод entry упрощенно выглядит примерно так:
    impl<K, V> HashMap<K, V> {
        pub fn entry<'a>(&'a mut self, key: K) -> Entry<'a, K, V>
        where
            K: Eq + std::hash::Hash,
        {
            use std::hash::Hasher as _;
            let mut hasher = self.get_hasher();
            key.hash(&mut hasher);
            let hash = hasher.finish();
    
            if let Some(elem) = self.table.find(hash, |(k, _)| key == *k) {
                Entry::Occupied(OccupiedEntry {
                    elem,
                    table: &mut self.table,
                })
            } else {
                Entry::Vacant(VacantEntry {
                    hash,
                    key,
                    table: &mut self.table,
                })
            }
        }
    
        fn get_hasher(&self) -> impl std::hash::Hasher {
            todo!()
        }
    }
    
    impl<T> Table<T> {
        fn find(&self, hash: u64, is_match: impl FnMut(&T) -> bool) -> Option<Bucket<T>> {
            todo!()
        }
    }

    Как видим мутабельная ссылка всё же есть, только она завернута в структуру, так как одной этой ссылки не достаточно, так как в случае свободной Entry нам нужно хранить ещё и ключ, а заодно и хэш (чтоб не считать его снова), а в случае занятой - указатель на бакет (область памяти где храниться пара ключ и значение).
    Ответ написан
    Комментировать
  • Как работает этот код?

    @Shaman_RSHU
    Ваш вопрос касается внутреннего устройства HashMap в Rust и особенностей работы с переменными и ссылками в Rust. Давайте разберемся, как работает HashMap и почему изменение Entry приводит к изменению хэш-таблицы.
    HashMap в Rust хранит пары ключ-значение. Когда вы вызываете map.entry(key), вы получаете Entry, который представляет собой перечисление (enum) с двумя вариантами: Occupied и Vacant. Этот Entry является оберткой над внутренним состоянием HashMap, позволяющей вам безопасно взаимодействовать с элементами хэш-таблицы.
    Когда вы вызываете map.entry(key), вы получаете Entry, который является отдельным объектом. Однако, важно понимать, что Entry не является отдельным объектом, который вы владеете в полном смысле владения в Rust. Вместо этого, Entry предоставляет вам доступ к внутренним данным HashMap через безопасный интерфейс.
    Изменение Entry и изменение HashMap
    Когда вы вставляете значение в Entry с помощью entry.insert(value), вы фактически изменяете внутреннее состояние HashMap. Это происходит потому, что Entry предоставляет вам доступ к внутренним данным HashMap для изменения. Внутренние данные HashMap хранятся в куче, и Entry обеспечивает безопасный доступ к ним.

    map.entry(10) возвращает Entry, который предоставляет вам доступ к внутренним данным HashMap для изменения. Это не мутабельная ссылка, потому что Entry не является просто ссылкой на данные. Вместо этого, Entry является оберткой, которая предоставляет безопасный интерфейс для взаимодействия с данными.

    Ваше понимание того, что Entry является отдельным объектом, который вы владеете, не совсем точно. Entry предоставляет вам доступ к внутренним данным HashMap для изменения, но не является отдельным объектом, который вы владеете. Изменение Entry приводит к изменению HashMap, потому что Entry обеспечивает безопасный доступ к внутренним данным HashMap.

    В Rust, когда вы работаете с переменными и ссылками, важно понимать, что владение и заимствование являются ключевыми концепциями, которые обеспечивают безопасность памяти. В случае с HashMap и Entry, Rust обеспечивает безопасность, предоставляя вам безопасный интерфейс для взаимодействия с внутренними данными.
    Ответ написан
    Комментировать
  • Как работают пакеты и крейты?

    bingo347
    @bingo347
    Crazy on performance...
    Крэйт - это в первую очередь дерево модулей. Каждый крэйт содержит как минимум 1 корневой модуль (обычно это main.rs или lib.rs, но так же это могут быть модули доп бинарников, модули интеграционных тестов, модули примеров). Так же к крэйту относятся модули, которые объявили в других модулях этого крейта (ключевое слово mod).
    Помимо этого крэйт - это сущность которой оперирует компилятор rustc, крэйт является единицей компиляции, то есть в rustc на компиляцию попадает крэйт целиком (на вход подаём корневой модуль, а он уже сам бегает по всему дереву согласно объявлениям mod).

    Пакет - это сущность которой оперирует cargo. Компилятор rustc ничего не знает про пакеты. По простому пакет это папка с файлом Cargo.toml, в котором есть секция package (бывают ещё Cargo.toml объявляющие только workspace). Пакет состоит из крейтов, притом должен быть как минимум 1 крейт бинарника или библиотеки, а библиотечный крейт может быть только 1 или отсутствовать вовсе.
    Пакет - это то, что публикуется в registry (такие как crates.io).
    Так же в зависимостях мы указываем именно пакеты (но только те, что содержат крэйт-библиотеку).
    Так же именно пакеты указываются в команде cargo install, при этом будут собраны все бинарные крейты входящие в пакет, а получившиеся исполняемые файлы будут помещены .cargo/bin
    Ответ написан
    2 комментария
  • Доступ к массиву объявленному в ассемблере из Си?

    @none7
    Это с точки зрения Ассемблера array это адрес. А с точки зрения Си uint32_t* array это глобальная статическая переменная в которой лежит указатель на uint32_t или соответствующий массив . То, что в Ассемблерах выглядит примерно так array_ptr dd offset array. А так как это bss, то естественно, что в этой переменной лежит ноль.
    Ответ написан
    4 комментария
  • Как устранить ошибку конверсии при использовании битовых полей?

    jcmvbkbc
    @jcmvbkbc
    "I'm here to consult you" © Dogbert
    Как устранить ошибку конверсии при использовании битовых полей?

    Не использовать битовые поля. Серьёзно. Удобств на копейку а мороки на рубль.
    Ответ написан
    4 комментария
  • Как правильно удалять элементы хэш таблицы?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Никак. Конечно, можно проверить, что там дальше ячейка пустая через k*k для всех возможных k (или что ячейка на (k-1)^2 назад пуста), но это слишком долго. И не сработает во всех случаях. Поэтому так и не делают вообще. Обычно "удаленные" значения убирают при перехешировании, которое все-равно придется делать при достаточном заполнении таблицы.
    Ответ написан
    3 комментария
  • Хэш-таблица без разрешения коллизий?

    wataru
    @wataru Куратор тега Алгоритмы
    Разработчик на С++, экс-олимпиадник.
    Нет. Ну, только если вы не будете заводить таблицу на 4 миллиарда с копейками элементов (2^32) и использовать тривиальную хеш-функцию.

    Потому что важно не столько количество элементов в таблице, а их значения. Их может быть 4 миллиарда различных. И даже только с 2 элементами я вам для любого меньшего размера таблицы найду 2 таких элемента, что у них хеш функция совпадет.

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