LINUX.ORG.RU

Rust - slice и кое-что ещё

 


0

3

Всем здравствуйте! Стыдно признаться, но сейчас понял, что я не знаю как получить f32 из slice взятого от Vec<u8>. В общем, история такая, использую crate serial, читаю от сферического прибора в вакууме данные, согласно сниферу тут всё отлично:

    let mut buf: Vec<u8>;
    let res = port.read(&mut buf[..]);

И я знаю, что мне приходит пакет следующего содержания:

01 04 04 00 05 92 fe 07 65

И что кусок <00 05 92 fe> - это f32. Так вот, как это засунуть то в f32?! Лучшее что пришло в голову - это

    let x: f32 = &buf[3..7] as f32;
    println!("V1: {}", x);
Но к сожалению не катит =\

★★★★
Ответ на: комментарий от slovazap

А сдвигами и битовыми операциями как всегда - не?

Ну это же не Си. Я так думаю, что в Rust должна быть своя языковая конструкция, чтобы не городить эти сдвиги

AntonyRF ★★★★
() автор топика

Для этого есть библиотечка parse
Можно еще через mem::transmute попробовать, но это небезопасно и потому что ансейф и потому что нет гарантии что там будет валидный f32.
Ну или сдвигами, да, с ручными проверками.

mersinvald ★★★★★
()
Последнее исправление: mersinvald (всего исправлений: 1)
Ответ на: комментарий от mersinvald

Вроде, то что нужно! Спасибо =)

AntonyRF ★★★★
() автор топика
Ответ на: комментарий от AntonyRF

Схренали? Языковые конструкции и в C есть, только они непортабельны относительно endianess и выравнивания. Ты собираешь двойное слово из отдельных байт в известном порядке - никакого более прямолинейного, однозначного и портабельного способа сделать это нежели чем сдвигами и битовыми операциями нет и не должно быть.

slovazap ★★★★★
()
Ответ на: комментарий от anonymous

Бгааа! Вся суть раста.

И что в этом плохого? Ну и byteorder делает немного больше, чем сдвиги.

DarkEld3r ★★★★★
()
Ответ на: комментарий от anonymous

Никто не мешает делать это руками/сдвигами без unsafe.

RazrFalcon ★★★★★
()
Ответ на: комментарий от slovazap

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

AntonyRF ★★★★
() автор топика
Ответ на: комментарий от AntonyRF

Вам об этом еще в третьем комменте писали. Работает так же, как и сишные касты. В общем случае - не безопасно.

RazrFalcon ★★★★★
()
Ответ на: комментарий от RazrFalcon

Ну писать то писали, но не обратил внимания. Уже загуглил.

AntonyRF ★★★★
() автор топика
Ответ на: комментарий от AntonyRF

Правильно будет так:

use std::mem::transmute;
...
let mut x;
let mut arrbuf: [u8; 4] = [0; 4];
unsafe {
    arrbuf.clone_from_slice(&buf[3..7])
    x = transmute::<[u8, 4], f32>(arrbuf)
};
println!("V1: {}", x);

Ибо со слайсом напрямую не прокатит.

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

vsemnazlo
()
Ответ на: комментарий от vsemnazlo

Это тоже не безопасно. [u8; 4] не гарантирует что данные будут выровнены по 4 байта. Надежнее копировать в u32 как это делает byteorder. Полный пример получится чуть больше:

use std::mem::transmute;
use std::ptr::copy_nonoverlapping;

fn read_f32(buf: &[u8]) -> f32 {
    unsafe { transmute(read_u32(buf)) }
}

fn read_u32(buf: &[u8]) -> u32 {
    assert!(4 <= buf.len());
    let mut data: u32 = 0;
    unsafe {
        copy_nonoverlapping(buf.as_ptr(), &mut data as *mut u32 as *mut u8, 4);
    }
    data.to_le()
}

fn main() {
    let buf = b"abcdefgh";
    let x: f32 = read_f32(&buf[3..7]);
    println!("V1: {}", x);
}

pftBest ★★★★
()
Ответ на: комментарий от pftBest

Разве [u8] не является непрерывным массивом байтов? Или в чем разница по выравниванию между u32 и [u8; 4]? Вот для record пришлось бы использовать

#[repr(packed)]
struct Data {
    ...
}
vsemnazlo
()
Ответ на: комментарий от vsemnazlo

ну разница в том что u8 имеет выравнивание 1, и значит [u8; 4] тоже 1. А f32 и u32 имеют выравнивание 4.

pftBest ★★★★
()
Ответ на: комментарий от vsemnazlo

Вот простая программа для проверки:

fn main() {
    use std::mem;
    println!("u8 {}", mem::align_of::<u8>());
    println!("u32 {}", mem::align_of::<u32>());
    println!("f32 {}", mem::align_of::<f32>());
    println!("[u8; 4] {}", mem::align_of::<[u8; 4]>());
    println!("[u32; 3] {}", mem::align_of::<[u32; 3]>());
}
Она выводит:
u8 1
u32 4
f32 4
[u8; 4] 1
[u32; 3] 4

pftBest ★★★★
()
Ответ на: комментарий от pftBest

Но на работу transmute для данной задачи это как должно влиять?

Вот это работает:

use std::mem::transmute;

fn print_vec_u8(data: &Vec<u8>, name: &str) {
    print!("{} =", name);
    for b in data {
        print!("{:5}", b);
    }
    println!("");
}

fn main() {
    let mut buf: Vec<u8> = vec![0; 8];
    let f1: f32 = 3.141592;
    let f2;
    println!("f1 = {}", f1);
    print_vec_u8(&buf, "(1) buf");

    /// f32 => Slice
    let tbf: [u8; 4];
    unsafe {
        tbf = transmute::<f32, [u8; 4]>(f1);
    };
    buf[3..7].clone_from_slice(&(tbf.iter().map(|b| *b).collect::<Vec<u8>>()));
    print_vec_u8(&buf, "(2) buf");

    /// Slice => f32
    let mut tbf2: [u8; 4] = [0; 4];
    tbf2.clone_from_slice(&buf[3..7]);
    unsafe {
        f2 = transmute::<[u8; 4], f32>(tbf2);
    };

    println!("f2 = {}", f2);
}
$ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.0 secs
     Running `target/debug/transtest`
f1 = 3.141592
(1) buf =    0    0    0    0    0    0    0    0
(2) buf =    0    0    0  216   15   73   64    0
f2 = 3.141592
vsemnazlo
()
Ответ на: комментарий от vsemnazlo

Оно работает потому повезло, а не потому что так правильно делать. Так получается что сейчас компилятор положил этот массив по кратному адресу, а может и не положить. Плюс процессор x86 не считает ошибкой доступ по невыровненому адресу, но на других системах, например на arm или powerpc это может упасть. Еще с точки зрения компилятора тут может быть неопределенное поведение, и соответственно такой код может быть неправильно соптимизирован.

pftBest ★★★★
()
Ответ на: комментарий от pftBest

Не влияет. Или как вызвать «невезение» для проверки? С неупакованной структурой был бы номер, да. UB здесь неоткуда взяться с такими типами. Что касается других архитектур, это должно быть заботой компилятора, вроде как.

vsemnazlo
()
Ответ на: комментарий от vsemnazlo

в сравнении с си, с++ и жавой очень бросается в глаза обилие знаков пунктуации и какая-то угловатость языка.

вот например:

let f1: f32 = 3.141592;
let tbf: [u8; 4];
unsafe {
    tbf = transmute::<f32, [u8; 4]>(f1);
};
у tbf и f1 вроде как объявлен типы, можно было бы сделать угадывание того, что туда можно вернуть и что-то сделать с двоеточиями.
let f1: f32 = 3.141592;
let tbf: [u8; 4];
unsafe {
    tbf = transmute(f1);
};

к тому же видны следы зубов не только перла но и паскаля. вот этот вот let. нахер он нужен? сделать переменные по умолчанию const - будет вам то же самое. Наконец, инициализация...

let f1: f32 = 3.141592;
ну вот кому присваивается 3.141592: f1 или f32? 21й век, корабли бороздят просторы, а тут паскаль.

ckotinko ☆☆☆
()
Последнее исправление: ckotinko (всего исправлений: 2)
Ответ на: комментарий от ckotinko

а тут паскаль.

тихо тихо. Паскаль не такой наркоманский.

anonymous
()
Ответ на: комментарий от ckotinko

можно было бы сделать угадывание того

Так оно и есть это угадывание, в реальном коде двоеточий не так часто встретишь. Просто в этом куске кода автор постарался расставить типы везде где только можно.

pftBest ★★★★
()
Ответ на: комментарий от ckotinko

можно было бы сделать угадывание того, что туда можно вернуть и что-то сделать с двоеточиями

Можно. Вывод типов вполне себе работает:

    let f1 = 3.141592_f32;
    let tbf: [u8; 4];
    unsafe {
        tbf = transmute_copy(&f1);
    };

вот этот вот let. нахер он нужен?

Для объявления переменных. И, да, они const по умолчанию.

ну вот кому присваивается 3.141592: f1 или f32?

Можно и по другому написать (см. выше). Вопрос вкуса.

red75prim ★★★
()
Ответ на: комментарий от ckotinko

О да, это критерий - обилие небуквенных символов... Остальное также совершенно мимо, если интересно, то читать руководство.

vsemnazlo
()
Ответ на: комментарий от red75prim

ну значит всё не так плохо, как я сперва подумал. во всех примерах видел именно наркоманскую запись с <>.

ckotinko ☆☆☆
()

А вот в дишечке это спокойно делается искаропки.

ubyte[] a = [1, 2, 3, 0, 0, 1, 15, 8, 9];
a[3..7].bigEndianToNative!uint.writeln; //271 

wolph ★★
()
Последнее исправление: wolph (всего исправлений: 1)
Ответ на: комментарий от vsemnazlo

UB здесь неоткуда взяться с такими типами.

Я сходил на irc и спросил у разработчиков, мне сказали что в плохом случае transmute сделает memcpy автоматически.

А потом я залез в доку и там прямо по белому и написано что transmute это memcpy:

/// `transmute` is semantically equivalent to a bitwise move of one type
/// into another. It copies the bits from the source value into the
/// destination value, then forgets the original. It's equivalent to C's
/// `memcpy` under the hood, just like `transmute_copy`.

Так что можно спать спокойно, UB не будет.

pftBest ★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.