Is it safe to construct a reference to a part of data with a pointer to all data and the identifier of which part of the data?

palyground

mod lib {
    use std::{sync::Arc, ops::Deref};

    #[derive(Debug)]
    pub struct Inner(pub DataType);

    #[derive(Debug)]
    pub enum DataType {
        A,
        B,
        C,
    }

    pub struct Data {
        pub a: Inner,
        pub b: Inner,
        pub c: Inner,
    }

    pub struct DataPartRef {
        ptr: Arc<Data>,
        ty: DataType,
    }

    pub fn part_ref(data: Arc<Data>, ty: DataType) -> DataPartRef {
        DataPartRef { ptr: data.clone(), ty }
    }

    impl Deref for DataPartRef {
        type Target = Inner;
        
        fn deref(&self) -> &Inner {
            match self.ty {
                DataType::A => &self.ptr.a,
                DataType::B => &self.ptr.b,
                DataType::C => &self.ptr.c,
            }
        }
    }
}

fn main() {
    use std::sync::Arc;
    use lib::*;
    let data = Arc::new(Data {
        a: Inner(DataType::A),
        b: Inner(DataType::B),
        c: Inner(DataType::C),
    });
    let part = part_ref(data, DataType::A);
    dbg!(&*part);
}

"Safe" means that no other part of the data can be accessed through the reference.

In Rust terms, if it compiles without use of the unsafe keyword, then it is guaranteed to be safe. In this case Arc ensures the ptr will remain valid for as long as it exists, and Deref only borrows it temporarily with assurances of the borrow-checker, so all is fine.

It would be riskier if you used raw pointers or made a self-referential type (using references instead of an enum), but these can't cause unsafety without use of unsafe{}. For dangerous implementations, there's cargo miri test that can catch some safety issues.


As for hiding data, the references don't allow pointer arithmetic, so well-behaved Rust code can't look at bytes it didn't borrow, and can't access private fields outside of their module. So it's safe from accidentally looking at fields it's not supposed to.

However, it depends what you're trying to protect from. Rust is not a sandbox language, so if your library is used inside someone else's binary, it can't protect anything. Malicious code running inside of your process can do whatever it wants, and get access to private fields, and violate all of Rust's rules.