Используя переменную смещения для отслеживания следующего элемента в массиве ржавчины


Я использую offset переменная для отслеживания итерации установки и изменения / возврата. Это ниже считается плохой практикой в Русте? Я спрашиваю это, потому что я не вижу очень часто разыменование в коде, которые я видел.

fn get_next_u16(input: &[u8], offset: &mut usize) -> u16 {
    let next = ((input[*offset+1] as u16) << 8) +
                (input[*offset]   as u16);
    *offset += 2;
    next
}

fn main() {
    let input = [1, 2, 3, 4];
    let mut offset = 0;
    let first_u16 = get_next_u16(&input, &mut offset);
    let second_u16 = get_next_u16(&input, &mut offset);
    println!("first: {}", first_u16);
    println!("second: {}", second_u16);
}

Я думаю, что делать это переназначить переменную смещения как:

fn get_next_u16(input: &[u8], offset: usize) -> (usize, u16) {
   (offset+2, ((input[offset+1] as u16) << 8) +
               (input[offset]   as u16))
}

fn main() {
    let input = [1, 2, 3, 4];
    let offset = 0;
    let (offset, first_u16) = get_next_u16(&input, offset);
    let (offset, second_u16) = get_next_u16(&input, offset);
    println!("first: {}", first_u16);
    println!("second: {}", second_u16);
}

Это выглядит чище, но теперь вам придется переназначить, чтобы компенсировать все время.

Возможно, есть другой способ сделать это?



Комментарии
3 ответа

Сравнение вариантов

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

let first_u16 = get_next_u16(&input, &mut offset);
let second_u16 = get_next_u16(&input, &mut offset);
let third_u16 = get_next_u16(&input, &mut offset);
let fourth_u16 = get_next_u16(&input, &mut offset);
....
let one_hundred_u16 = get_next_u16(&input, &mut offset);

input и offset тесно взаимосвязаны, что показывает, что что-то неладно. В структуречес способ справиться и в то же время было бы здорово. Или мы могли бы найти некоторую закрытие где-то еще.

Последний вариант явно показывает пользователю, что есть больше информации, для работы с: usize не стоит игнорировать! В конце концов, это часть производства. Но увы, это все еще подвержены ошибкам, поскольку мы теперь можете случайно использовать то же offset дважды:

let (offset2,  first_u16) = get_next_u16(&input, offset);
let (_ , second_u16) = get_next_u16(&input, offset); // whoops!

Это связано с тем, что usize реализует Copy черта.

Перебирать с итераторами

Но давайте сделаем шаг назад: вы хотите безопасный способ, чтобы получить следующий u16 неоднократно. Вы хотите итератор. Ломтики уже представить сторона итератор , называемые фрагментами. Таким образом, мы можем сделать два u8легко. Все, что нам нужно сделать, это сопоставить эти куски:

fn to_u16(input: &[u8]) -> u16 {
((input[1] as u16) << 8) + (input[0] as u16)
}

fn main() {
let input = [1, 2, 3, 4];
let output: Vec<_> = input.chunks(2).map(to_u16).collect();
println!("{:?}", output);
}

Обратите внимание, что это будет паники, если input не содержат четное число u8С. Вы можете использовать нестабильная chunks_exact функция или возвращает Option<u16> вместо.

Другие замечания

Использовать rustfmt для форматирования кода. Таким образом, ваш код выглядит примерно одну написанные другими.

3
ответ дан 9 апреля 2018 в 01:04 Источник Поделиться

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

Лучшее решение для вас, чтобы скрыть это offset в struct и вызвать метод этого struct. При каждом звонке, смещение обновляется, и вы получаете результат. Итераторы предназначены именно для такого использования:

struct U16Generator<'a> {
input: &'a [u8],
offset: usize,
}

impl<'a> U16Generator<'a> {
fn new(input: &'a [u8]) -> Self {
U16Generator { input, offset: 0 }
}
}

impl<'a> Iterator for U16Generator<'a> {
type Item = u16;

fn next(&mut self) -> Option<Self::Item> {
match (self.input.get(self.offset), self.input.get(self.offset + 1)) {
(Some(u1), Some(u2)) => {
self.offset += 2;
Some(((*u2 as u16) << 8) + *u1 as u16)
},
_ => None,
}
}
}

#[test]
fn test() {
let mut gen = U16Generator::new(&[1, 2, 3, 4]);

assert_eq!(gen.next(), Some(513));
assert_eq!(gen.next(), Some(1027));
}

Один положить все в struct (коллекции и смещение), и при каждом вызове следующий, одно обновление offset затем вернуть правильное значение.


Обратите внимание, что этот ответ является образовательной. Очевидно, что для такого простого использования, это лучше использовать Дзета Решение: input.chunks(2).map(to_u16).

2
ответ дан 9 апреля 2018 в 01:04 Источник Поделиться

А срез - это указатель и длину. Указатель не обязательно должен указывать на первый элемент массива! Таким образом, вместо поддержания смещением, можно обновить срез.

fn get_next_u16(input: &mut &[u8]) -> u16 {
let next = ((input[1] as u16) << 8) +
(input[0] as u16);
*input = &input[2..];
next
}

fn main() {
let mut input = &[1u8, 2, 3, 4] as &[_];
let first_u16 = get_next_u16(&mut input);
let second_u16 = get_next_u16(&mut input);
println!("first: {}", first_u16);
println!("second: {}", second_u16);
}

2
ответ дан 10 апреля 2018 в 01:04 Источник Поделиться