Обработка потокового итератора как обычного итератора с использованием PhantomData и unsafe

Я знаю, что приведенный ниже код является хакерским, но можно ли его назвать безопасным и идиоматичным Rust? Есть ли лучший способ для этого?

// needs to do 'rustup default nightly' to run under valgrind
// #![feature(alloc_system, global_allocator, allocator_api)]
// extern crate alloc_system;
// use alloc_system::System;
// #[global_allocator]
// static A: System = System;

struct Foo<'a> {
    v: Vec<u8>,
    pos: usize,
    phantom: std::marker::PhantomData<&'a u8>,
}

impl<'a> Iterator for Foo<'a> {
    type Item = &'a mut u8;

    fn next(&mut self) -> Option<&'a mut u8> {
        let r = self.v.get_mut(self.pos);
        if r.is_some() {
            self.pos += 1;
            unsafe { Some(&mut *(r.unwrap() as *mut u8)) }
        } else {
            None
        }
    }
}

impl<'a> Foo<'a> {
    fn reset(&mut self) {
        self.pos = 0;
    }
}

fn main() {
    let mut x = Foo {
        v: (1..10).collect(),
        pos: 0,
        phantom: std::marker::PhantomData,
    };
    let vp = x.v.as_ptr();

    {
        for i in &mut x {
            println!("{}", i);
        }
    }
    {
        x.reset();
    }
    {
        for i in &mut x {
            *i *= *i;
        }
    }
    {
        x.reset();
    }
    {
        for i in &mut x {
            println!("{}", i);
        }
    }

    assert!(vp == x.v.as_ptr());
}

Напишите немного в комментарии, Valgrind мне сказал, что утечки нет и результат соответствует ожиданиям под Rust 1.26.0-nightly и 1.25.0.

Связанный:


person chamaken    schedule 06.04.2018    source источник
comment
Всякий раз, когда вы пишете блок unsafe, я настоятельно призываю людей включать комментарий к блоку, объясняющий, почему вы считаете, что код на самом деле безопасен. Этот тип информации полезен для людей, которые будут читать код в будущем, и будет полезен любому потенциальному ответчику. Пожалуйста, отредактируйте свой вопрос, чтобы объяснить, почему вы считаете, что этот код на самом деле безопасен.   -  person Shepmaster    schedule 06.04.2018
comment
Спасибо за ваш всегда оперативный ответ. Говоря о unsafe, я не подумал, извините. Я понимаю, что просто пытаюсь обмануть программу проверки жизни. valgrind фактически сообщает, что ваш example() код недействителен.   -  person chamaken    schedule 06.04.2018
comment
Нет никакого способа делать то, что вы хотите (и поверьте мне, я пытался). Вы должны дождаться общих связанных типов и обновления итераторов Rust.   -  person Boiethios    schedule 06.04.2018


Ответы (1)


Этот код небезопасен. Пользователь типа может выбрать любое время жизни, включая 'static:

fn constructor() -> Foo<'static> {
    Foo {
        v: vec![42; 10],
        pos: 0,
        phantom: std::marker::PhantomData,
    }
}

fn example() -> &'static u8 {
    let mut f = constructor();
    f.next().unwrap()
}

fn main() {
    println!("example: {}", example());
}

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


В другом вопросе и ответе есть пример того, как можно написать этот код без unsafe.

person Shepmaster    schedule 06.04.2018