-1

I know the code below is hacky, but could it be called safe and idiomatic Rust? Is there better way for this?

// 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());
}

Write a little bit in the comment, Valgrind told me no leak and the result is as expected under Rust 1.26.0-nightly and 1.25.0.

Related:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
chamaken
  • 81
  • 5
  • 3
    Whenever writing an `unsafe` block, I *strongly* encourage people to include a comment on the block explaining *why you think the code is actually safe*. That type of information is useful for the people who read the code in the future, and will be useful to any potential answerer. Please [edit] your question to explain why **you** think this code is actually safe – Shepmaster Apr 06 '18 at 01:41
  • Thank you for your always prompt response. Talking about `unsafe`, I have no thought, sorry. I recognize I just try to cheat lifetime checker. valgrind actually reports your `example()` code read invalid. – chamaken Apr 06 '18 at 03:34
  • There is **no way** to do what you want to do (and believe me, I tried). You must wait for generic associated types and an update of Rust iterators. – Boiethios Apr 06 '18 at 08:51

1 Answers1

2

This code is not safe. The user of the type may choose any lifetime, including '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());
}

Here, example returns a reference to a variable that is no longer in scope, accessing invalid memory and subverting the restrictions you must uphold.


There's an example of how you could write this code with no unsafe whatsoever in another Q&A.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366