Ответы пользователя по тегу Rust
  • Как поделить api на части с различным функционалом?

    vabka
    @vabka Куратор тега Rust
    Лучше это не через поля, а через методы реализовывать - тогда будет более дёшево, да и женерик лишний можно будет убрать.

    Что-то типа
    struct Api {
      key: String
    }
    struct ApiPart<'a>{
      api: &'a Api
    }
    
    impl Api {
      pub fn part<'a>(&'a self) -> ApiPart<'a> {
        ApiPart {api: self}
      }
    }


    PS:
    Вот так делать точно не нужно:
    Не надо на ровном месте городить указатели и unsafe.
    У тебя из-за unsafe получился dangling pointer.
    fn new(api_key: *const String) -> Self {
            let api_key = unsafe { &*api_key };
            return Self {
                api_key,
            };
        }


    Норм практика делать вот так, в случае строк:
    fn new(api_key: impl Into<String>) -> Self {
            let api_key = api_key.into();
            return Self {
                api_key,
            };
        }


    Боксить строки также не нужно - они и так в куче лежат.
    Ответ написан
    6 комментариев
  • Куда утекает память в read Rust?

    vabka
    @vabka Куратор тега Rust
    TL;DR; 10400000 * 100 - это как раз почти гигабайт.
    В текущем виде это компилироваться не должно, тк as_slice возвращает ссылку, а чтобы структура могла содержать в себе ссылку - нужно добавить лайфтайм в объявление структуры => этот же лайфтайм нужно будет указать в сигнатуре функции.
    Сейчас ни того ни другого я не вижу и код не должен компилироваться по причине того что ты пытаешься вернуть ссылку на данные, которые живут только в рамках fn get.
    Если же BinValue на самом деле владеет вектором, то это не утечка, а вполне ожидаемое поведение.
    Смотри, где должен он дропаться в будущем.

    1. Тут можно достаточно легко избавиться от unsafe:
    let res = self.get_index_and_file(key);
    
    if res.is_none() {
        return None;
    }
    
    let (file, info) = unsafe { res.unwrap_unchecked() };


    Превращается в
    let Some((file,info)) = self.get_index_and_file(key) else {
      return None;
    };


    2.
    Вообще достаточно странный код.
    На этой строке мы создали вектор - это ок. Он требует места в куче. Хотя странно, что info.0 у нас u64, а не usize
    let mut buf = vec![0; info.0 as usize];
    file.read().unwrap().read_at(info.1, &mut buf).expect("failed to read");

    Тут мы файл прочитали. Возможно был смысл сделать BufRead, но не думаю, что он тут бы тут много чего сделал бы.
    В остальном тут нет аллокаций.

    3. А вот это уже выглядит реально подозрительно:
    return Some(BinValue::new(buf.as_slice()));
    Это вообще компилируется? Покажи, что из себя представляет BinValue.
    По хорошему оно должно брать ownership над вектором.


    Если оставить код таким, то через 10 400 000 вызовов этой функции с заполнение буфера 100-байтовыми значениями утечёт примерно 1 ГБ памяти

    10 400 000 * 100 = как раз примерно гиг.
    Если BinValue в действительности берёт ownership, то это вполне себе ожидаемое поведение, а не утечка.

    4. Ещё я бы на всякий случай убрал #[inline(always)]
    Ответ написан
  • Какие существуют кольцевые lock free очереди / каналы для Rust?

    vabka
    @vabka Куратор тега Rust
    Если используешь async, то tokio::sync::mpsc - в токио нет выделенного spsc, так что для таких сценариев рекомендуют его.
    Если допустимо терять какие-то данные, то можешь tokio::sync::watch попробовать.

    Если не используешь async, то блокировка будет, если канал пуст.
    Но в принципе можешь попробовать crossbeam.

    Если нужно настоящее кольцо, то вроде есть крейты с соответствующим названием, но их я не пробовал
    Ответ написан
    Комментировать
  • Какой Rust GUI лучше для прода в 2024?

    vabka
    @vabka Куратор тега Rust
    нынче развелось довольно много

    Не так уж и много.

    Наиболее близкое к проду, кмк - tauri из-за коммьюнити, хорошей документации, и гибкости, тк html+css.

    Если тебе допустимо immediate mode и внешний вид не сильно важен (к примеру какая-то небольшая утилита или ui для отладки в игре), то egui тоже норм.
    Ответ написан
    4 комментария
  • Какой сайт с задачами rust?

    vabka
    @vabka Куратор тега Rust
    1. Не сайт, но сборник задач: Rust koans. По сути своей - интерактивный учебник по синтаксису языка.
    (UPD: на самом деле я думал про rustlings - это официальный сборник задач, а не koans)
    2. Когда уже уверенно можешь писать код/знаком с синтаксисом - можешь попрактиковаться на leetcode и codewars.

    Ну и лучше не на сайте сидеть, а нормальный редактор на компьютер установить, который будет давать подсказки по синтаксису и API.
    Ответ написан
    Комментировать
  • Как посоветуете выполнять развёртывание web сервера Rust на VDS?

    vabka
    @vabka Куратор тега Rust
    НО это неудобно и не правильно

    Почему?
    Ну вообще по хорошему да, тут бы ещё какой-нибудь nginx добавить, чтобы он проксировал запрос.
    А так вроде всё ок, если не упарываться контейнерами.

    с Докером пока единственная мысль это собирать образ локально и его docker push

    Если не хочется усложнять жизнь при помощи добавления какого-то билд-сервера, то это более чем рабочий вариант

    сразу скажу я в раст пришёл после разработки на скриптовых языках, там весь исходный код летит на ВДС

    Как ты правильно сказал - отправлять тут нужно только исполняемые файлы. А как же ты тогда раньше скриптовые языки запускал? Без systemd?
    Ответ написан
  • Как сделать collect в кортеж?

    vabka
    @vabka Куратор тега Rust
    Кортеж имеет фиксированный размер и по тому просто так collect сделать нельзя.
    Тут тебе три варианта:
    1. Вроде в itertools есть реализации collect для небольших гомогенных кортежей.
    2. Делай collect в Vec и бери элементы из него
    3. Сделай два раза next на итераторе

    Код

    раз
    fn main() {
        let items = [1,2];
        let mut iter = items.into_iter().map(|x|5+x);
        let a = iter.next().unwrap();
        let b = iter.next().unwrap();
        println!("{a} {b}");
    }


    Два:
    fn main() {
        let items = [1,2];
        let [a,b] = items.map(|x|5+x);
        println!("{a} {b}");
    }


    Три:
    use itertools::Itertools; // 0.9.0
    
    fn main() {
        let items = [1,2];
        let (a,b) = items.iter().map(|x|5+x).next_tuple().unwrap();
        println!("{a} {b}");
    }

    Ответ написан
    4 комментария
  • Что за тип Unit в Haskel Rust Kotlin? Чем отличается от Void?

    vabka
    @vabka Куратор тега Rust
    void означает, что функция ничего не возвращает. Такие функции не получится использовать в выражениях и их результат нельзя никуда присвоить. Из-за этого многие обобщённые вещи приходится писать по два раза: один раз для обычных функций и ещё раз для функций, которые возвращают void.
    Такие функции ещё называют процедурами.

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


    Можно ли увидеть аналог пример кода на c# java с этим типом?

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

    А еще, вот к примеру я придумал, почему нету(может этот тип и есть), К примеру в c# есть понятие Boxing типов при касте через object
    int a=(int)(object)1;
    Что довольно дорого и долго. С выделением памяти в куче.
    А почему нету Типа, который ни чего не упаковывает, а просто передает двойное машинной слово? Ведь это прекрасно покроет все простые структурные типы, int char double., Enum, кортежи в 8 байт. А все остальное уже как объект.

    1. А что делать если тебе боксинг явно нужен?
    2. А где ты будешь метаданные передавать? Как в такой ситуации отличить тот же long от ссылки на объект?
    3. По спецификации передаётся ссылка, а не двойное машинное слово.

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

    vabka
    @vabka Куратор тега Rust
    Нет, эти два примера кода будут делать абсолютно разные вещи.
    В первом случае ты возвращаешь String, который содержит в себе несколько полей: len, capacity, и указатель на буфер.
    Во втором случае ты String ещё в кучу копируешь и возвращаешь указатель на String в куче.

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

    Ведь нет какой-то волшебной операции вернуть владение

    Она не волшебная, и она существует. Только владение не возвращается, а передаётся.
    Но существует только на уровне типов.

    https://play.rust-lang.org/?version=stable&mode=re...

    Первая функция:
    pushq	%rbx
    	movq	%rdi, %rbx
    	movq	__rust_no_alloc_shim_is_unstable@GOTPCREL(%rip), %rax
    	movzbl	(%rax), %eax
    	movl	$5, %edi
    	movl	$1, %esi
    	callq	*__rust_alloc@GOTPCREL(%rip)
    	testq	%rax, %rax
    	je	.LBB10_1
    	movb	$114, 4(%rax)
    	movl	$1701736041, (%rax)
    	movq	%rax, (%rbx)
    	movq	$5, 8(%rbx)
    	movq	$5, 16(%rbx)
    	popq	%rbx
    	retq

    Вторая:
    pushq	%r14
    	pushq	%rbx
    	pushq	%rax
    	movq	__rust_no_alloc_shim_is_unstable@GOTPCREL(%rip), %r14
    	movzbl	(%r14), %eax
    	movl	$5, %edi
    	movl	$1, %esi
    	callq	*__rust_alloc@GOTPCREL(%rip)
    	testq	%rax, %rax
    	je	.LBB11_6
    	movq	%rax, %rbx
    	movb	$114, 4(%rax)
    	movl	$1701736041, (%rax)
    	movzbl	(%r14), %eax
    	movl	$24, %edi
    	movl	$8, %esi
    	callq	*__rust_alloc@GOTPCREL(%rip)
    	testq	%rax, %rax
    	je	.LBB11_2
    	movq	%rbx, (%rax)
    	movq	$5, 8(%rax)
    	movq	$5, 16(%rax)
    	addq	$8, %rsp
    	popq	%rbx
    	popq	%r14
    	retq
    Ответ написан
    6 комментариев
  • Как открывать окно tauri только после выполнения задачи?

    vabka
    @vabka Куратор тега Rust
    thread::spawn возвращает экземпляр JoinHandle у которого есть метод .join() - при его вызове основной поток будет заблокирован и будет ждать завершение потока, который надо заджоинить.
    fn main() {
        let thread = thread::spawn(|| {
            loop {
                process();
                thread::sleep(Duration::from_secs(3));
            }
        });
        thread.join().unwrap(); // но так как у нас в потоке будет loop - дальше выполнение не пойдёт никогда
        tauri::Builder::default()
            .invoke_handler(tauri::generate_handler![greet])
            .run(tauri::generate_context!())
            .expect("error while running tauri application");
    }


    Второй вариант - ты можешь использовать std::sync::mpsc и с помощью него уже передать сообщение, что нужно создать окно.
    Ответ написан
  • Как передать готовый хеш в хеш-таблицу?

    vabka
    @vabka Куратор тега Rust
    Используя стандартный HashMap максимум что ты можешь - сделать структуру, которая хранит в себе сам ключ и хэш от него, и реализовать для неё trait Hash - так ты обменяешь процессорные такты на память.

    Так что остаётся либо реализовать свою HashMap, либо попробовать реализовать алгоритм иначе.
    Ответ написан
  • Как оптимизировать код в rust при помощи trait?

    vabka
    @vabka Куратор тега Rust
    1. Действительно непонятно, для чего и как ты хочешь эти трейты использовать.
    2. Кажется, ты пытаешься переизобрести num
    3. Ошибка у тебя из-за того что ожидается какой-то неизвестный T, а ты пихаешь туда f64
    Тоесть, например, я бы мог захотеть сделать FloatStruct::<f32>::new() но вместо FloatStruct<f32> ты пытаешься вернуть мне FloatStruct<f64>
    Ответ написан
    8 комментариев
  • Как объявить тип, который является коллекцией значений разных типов, которые можно конвертировать в другие типы ❌️с помощью trait-а Into❌️?

    vabka
    @vabka Куратор тега Rust
    В твоём случае тебе нужно завести новый trait, который будет брать ссылку на оригинальный объект, в отличие от Into. Потом ты сможешь сделать blanket implementation наподобие
    impl<T: Clone + Into<JsValue>> MyIntoJsValue for T {
        fn into_jsvalue(&self) -> JsValue{
            self.clone().into()   
        }
    }
    Ответ написан
    4 комментария
  • Откуда взялась ассоциативная функция from?

    vabka
    @vabka Куратор тега Rust
    В wasm-bindgen::JsValue Нету impl From for JsValue

    Всё там есть https://docs.rs/virtual-dom-rs/latest/virtual_dom_...
    impl From<Element> for JsValue
    + есть целый ряд blanket impl

    Не работает. Работает только с &element

    Потому что ты реализовал трейт для &element, а не для element.
    Сам же в начале написал, что
    Тут тоже подвоха нет. get_element_by_id() возвращает Option<Element>, а не Option<&Element>

    Вот такое уже работает:
    https://play.rust-lang.org/?version=stable&mode=de...

    Но эти поганые макросы наносят уроны документации, и не видно какие именно есть поля и функции у JsValue.

    Макросы на это влиять не должны. VS Code с rust-analyzer и intellij rust умеют показывать все impl для структуры, в том числе blanket impl и те impl которые возникли в результате вызова макроса.
    Ответ написан
    1 комментарий
  • Как разделить Cargo.toml на несколько файлов?

    vabka
    @vabka Куратор тега Rust
    Разделить Cargo.toml на несколько файлов нельзя.
    Разделять features на несколько штук я тоже не рекомендую - читать это будет сложно с учётом того, сколько фич в web-sys и как ты скорее всего будешь их добавлять

    В самом web-sys это автогенеренный код, но всё же:
    https://github.com/rustwasm/wasm-bindgen/blob/main...
    Ответ написан
  • Может ли Раст полноценно заменить Джаву в сфере финансовых технологий?

    vabka
    @vabka Куратор тега Rust
    На Java пишется очень много разных вещей, в тч не требовательных к утилизации ресурсов и задержкам так что предположу, что многие вещи просто нецелесообразно переписывать.

    Кроме Java в финтехе вообще много разных языков используется. Даже такая экзотика как Haskell и F# бывает.

    Могу сказать, что сейчас работаю в большой корпорации и мы как раз пишем на Rust штуки, которые отвечают за работу с платёжными системами.

    Раньше примерно то же самое писалось на плюсах.

    На Java такое не писалось, тк авторизация транзакций должна меньше чем за секунду происходить после того как покупатель приложил карту к терминалу, и при этом нужно много разных проверок произвести.
    При этом многомиллионную ораву клиентов спокойно обслуживает не самый мощный сервер (емнип всего 32гб оперативки) с минимальным количеством сбоев.
    Ответ написан
    5 комментариев
  • Как получать в переменную значение из консоли и буфера обмена?

    vabka
    @vabka Куратор тега Rust
    Это уже и так из коробки работает в терминале. Не нужно усложнять своё приложение этим.
    Попробуй использовать shift+insert или ctrl+shift+v, если у тебя в терминале на ctrl+v пишется ^V.
    Либо посмотри настройки.
    Ответ написан
    Комментировать
  • Как собрать rust-крейт в среде MSYS2 с помощью cargo?

    vabka
    @vabka Куратор тега Rust
    Ошибка у тебя возникает из-за того что в Msys не видит Cargo, а не видит он его из-за того что его нет в PATH - это раз.

    Два:
    https://stackoverflow.com/questions/47379214/step-...
    Ответ написан
    Комментировать
  • Какой Object pool стоит использовать в Rust?

    vabka
    @vabka Куратор тега Rust
    Например https://crates.io/crates/deadpool

    Но конкретно в твоём примере не вижу нужды в пуле буферов.
    Ответ написан
  • Почему при передаче владения объектом другой переменной в rust меняется адрес объекта?

    vabka
    @vabka Куратор тега Rust
    Отчасти от того что в Rust нет такого понятия как "объект" - оно просто не нужно.
    Потому что ты сам взял адрес переменной.
    &mut s и &mut s1.
    Две разные переменные будут физически располагаться в разных местах => адреса у них будут разные
    Ответ написан