Immutable struct with mutable reference members - rust

Is my understanding correct that in Rust it is not possible to protect reference members of a struct from modification while having the reference target values mutable? (Without runtime borrow checking that is.) For example:
struct MyData<'a> {
pub some_ref: &'a mut i32,
}
fn doit<'a>(data: &mut MyData<'a>, other_ref: &'a mut i32) {
// I want to be able to do the following here:
*data.some_ref = 22;
// but make it impossible to do the following:
data.some_ref = other_ref;
}
Not being able to change the reference value may be useful in certain FFI situations. FFI and the performance requirements reasons prevent the use of runtime borrow checking here.
In C++ it can be expressed like this:
struct MyData {
int* const some_ref;
};
void doit(const MyData &data, int* other_ref) {
// this is allowed:
*data.some_ref = 22;
// this is not:
data.some_ref = other_ref; // compile error
}

You can create a wrapper type around the reference. If the constructor is private, and so is the wrapped reference field, you cannot replace the reference itself. You can then implement DerefMut to allow changing the referent.
pub struct ImmRef<'a> {
inner: &'a mut i32,
}
impl<'a> ImmRef<'a> {
fn new(inner: &'a mut i32) -> Self { Self { inner } }
}
impl std::ops::Deref for ImmRef<'_> {
type Target = i32;
fn deref(&self) -> &Self::Target { &*self.inner }
}
impl std::ops::DerefMut for ImmRef<'_> {
fn deref_mut(&mut self) -> &mut Self::Target { &mut *self.inner }
}
struct MyData<'a> {
pub some_ref: ImmRef<'a>,
}
fn doit<'a>(data: &mut MyData<'a>, other_ref: &'a mut i32) {
// I want to be able to do the following here:
*data.some_ref = 22;
// but make it impossible to do the following:
// data.some_ref = other_ref;
}
You can mark the newtype #[repr(transparent)] for FFI purposes.
But do note that if the code has some ImmRef<'a> available it can use tools such as std::mem::replace() to replace the reference.

Rust does not allow you to specify the mutability of individual fields like you can via const in C++. Instead, you should simply encapsulate the data by making it private and only allow modification through methods that you dictate:
struct MyData<'a> {
some_ref: &'a mut i32,
}
impl MyData<'_> {
pub fn set_ref(&mut self, other: i32) {
*self.some_ref = other;
}
}
That way, the field some_ref cannot be modified directly (outside of the module) and must use the available method.

Related

How to implement a trait for different mutabilities of self

Given is a struct with a u32 field. It has exactly one overloaded method that can be used as both setter and getter for the field.
pub struct Struct {
value: u32
}
pub trait Trait<T> {
fn method(&mut self, arg: T);
}
impl Trait<u32> for Struct {
fn method(&mut self, arg: u32) {
self.value = arg;
}
}
impl Trait<&mut u32> for Struct {
fn method(&mut self, arg: &mut u32) {
*arg = self.value;
}
}
A possible use of this structure could be as follows
fn main() {
let mut s = Struct { value: 0 };
let mut v = 0;
s.method(&mut v);
println!("The value of s is: {}", v);
s.method(3);
s.method(&mut v);
println!("The value of s is: {}", v);
}
The advantage of calling an overloaded method instead of accessing the field directly serves two reasons when the struct is used in a ffi interface. On the one hand, the method can still make modifications to the value, such as first converting a &str to a CString and then storing the *const u8 as the value, which makes the string usable for C. On the other hand, overloading the method also makes it possible to use the names given for it in C instead of writing setValue and getValue, for example. However, as you can see here, one of the two methods does not need a mutable reference to self because it does not change the value field, but because the trait requires it, it is used in both cases anyway. The struct is not only configured and then used as argument in a ffi method, but can also occur as return value of such a method, in which case it will be returned as a immutable reference and should only be read from. The customized trait implementations would look like this
impl Trait<u32> for Struct {
fn method(&mut self, arg: u32) {
self.value = arg;
}
}
impl Trait<&mut u32> for Struct {
fn method(&self, arg: &mut u32) {
*arg = self.value;
}
}
Obviously this won't work here because the second impl block doesn't have the same method signature as the trait. I already tried to define the self paramter as another generic parameter in the trait using the arbitrary_self_types features but unfortunately that didn't work.
You cannot parameterize over mutability.
See:
internals.rust-lang.org - Parameterisation over mutability.
internals.rust-lang.org - Generic mutability parameters.
How to implement a trait for any mutability?
How to avoid writing duplicate accessor functions for mutable and immutable references in Rust?
You can parameterize on self, but it will not be a method anymore, only an associated function:
pub trait Trait<This, T> {
fn method(this: This, arg: T);
}
impl Trait<&mut Self, u32> for Struct {
fn method(this: &mut Self, arg: u32) {
this.value = arg;
}
}
impl Trait<&Self, &mut u32> for Struct {
fn method(this: &Self, arg: &mut u32) {
*arg = this.value;
}
}
fn main() {
let mut s = Struct { value: 0 };
let mut v = 0;
Struct::method(&s, &mut v);
println!("The value of s is: {}", v);
Struct::method(&mut s, 3);
Struct::method(&s, &mut v);
println!("The value of s is: {}", v);
}
Given the draft RFC Refined trait implementations that poses the possibility to refine a trait in its implementation, i.e. to write a less generic impl (e.g. safe method in the impl that is unsafe in the trait), it may be possible that you will be able to refine mutability in the future too (although I haven't seen discussions about that), but that will only relax the restriction when you work with a concrete instance, not with generics.
One way or the other, I don't think this design is correct. Just use normal value() and set_value() methods, it is simple and obvious.

Passing on lifetimes of underlying reference fields?

I'm trying to make two structs that operate on an underlying dataset; one providing immutable "read" operations, the other allowing modification. For this to work, I need to be able to use the read functions from within the modifying object - as such I create a temporary new read object within the modifier function with a view onto the underlying data.
Here's some code:
struct Read<'db> {
x: &'db i32
}
impl<'db> Read<'db> {
pub fn get(&'db self) -> &'db i32 { self.x }
}
struct Write<'db> {
x: &'db mut i32
}
impl<'db> Write<'db> {
fn new(x: &mut i32) -> Write { Write{x: x} }
fn as_read(&'db self) -> Read<'db> {
Read{x: self.x}
}
pub fn get(&'db self) -> &'db i32 { self.as_read().get() }
}
fn main() {
let mut x = 69i32;
let y = Write::new(&mut x);
println!("{}", y.get());
}
It doesn't compile - it seems that despite my best efforts, the lifetime of the reference returned from Read::get is bound to the Write::get's scope, rather than the Write's 'db lifetime. How can I make it compile? (And, is what I want to do possible? Is it the simplest/most concise way of doing it?)
The point the compiler is trying to get across is that &'db self actually means self: &'db Write<'db>. This means that you tie the reference AND the type to the same lifetime. What you actually want in your case is self: &'a Write<'db> where 'a lives just for the as_read function. To be able to return a 'db reference from a 'a reference, you need to specify that 'a lives at least as long as 'db by constraining 'a: 'db.
fn as_read<'a: 'db>(self: &'a Write<'db>) -> Read<'db> {
Read{x: self.x}
}
pub fn get<'a: 'db>(self: &'a Write<'db>) -> &'db i32 { self.as_read().get() }
or more concisely
fn as_read<'a: 'db>(&'a self) -> Read<'db> {
Read{x: self.x}
}
pub fn get<'a: 'db>(&'a self) -> &'db i32 { self.as_read().get() }
Try it out in the Playground

How can I expose a safe wrapper around an owned pointer?

I'm wrapping a C library that has two structs: one has a pointer to the other.
struct StructA {
void * some_mem;
};
struct StructB {
void * some_mem;
struct StructA * some_struct;
};
Both of these structs own memory, so my wrapper has constructors and destructors for both of them.
struct StructA(*mut c_void);
impl StructA {
fn new() -> Self {
StructA(c_constructor())
}
}
impl Drop for StructA {
fn drop(&mut self) {
let StructA(ptr) = self;
c_destructor(ptr);
}
}
There's also a function that takes a pointer to StructB and returns its pointer to StructA:
const struct StructA * get_struct(const struct StructB * obj);
The user of this function should not free the returned pointer, since it will be freed when the user frees obj.
How can I wrap this function? The problem is that the destructor for StructB frees all its memory, including the one for StructA. So if my wrapping of get_struct returns an object, then the wrapped StructA will be freed twice (right?). It could instead return a reference to an object, but where would that object live?
I could have separate structs for StructA based on whether it's standalone and needs to be freed or if it's a reference, but I'm hoping that's unnecessary.
I could have separate structs for StructA based on whether it's standalone and needs to be freed or if it's a reference, but I'm hoping that's unnecessary.
It's necessary. The difference between an owned StructA * and a borrowed StructA * is precisely the same as the difference between a Box<T> and a &T. They're both "just a pointer", but the semantics are completely different.
Something along these lines is probably what you want:
use std::marker::PhantomData;
struct OwnedA(*mut c_void);
impl Drop for OwnedA {
fn drop(&mut self) { }
}
impl OwnedA {
fn deref(&self) -> RefA { RefA(self.0, PhantomData) }
}
struct RefA<'a>(*mut c_void, PhantomData<&'a u8>);
struct OwnedB(*mut c_void);
impl Drop for OwnedB {
fn drop(&mut self) { }
}
impl OwnedB {
fn get_a(&self) -> RefA { RefA(get_struct(self.0), PhantomData) }
}
In particular, it's worth noting that lifetime parameter on RefA lets the compiler make sure you don't use a RefA after the backing structure has been freed.
I could have separate structs for StructA based on whether it's standalone and needs to be freed or if it's a reference, but I'm hoping that's unnecessary.
I believe this would be the accepted pattern. For backup, I'd point to the fact that this is a normal pattern in the Rust library. &str and String, &[T] and Vec<T>, Path and PathBuf, and probably lots of others I can't think of.
The good news is that you can use similar patterns as these pairs, leveraging Deref or DerefMut to call down to shared implementation:
use std::ops::{Deref, DerefMut};
enum RawFoo {}
fn c_foo_new() -> *const RawFoo { std::ptr::null() }
fn c_foo_free(_f: *const RawFoo) {}
fn c_foo_count(_f: *const RawFoo) -> u8 { 42 }
fn c_foo_make_awesome(_f: *const RawFoo, _v: bool) { }
struct OwnedFoo(Foo);
impl OwnedFoo {
fn new() -> OwnedFoo {
OwnedFoo(Foo(c_foo_new()))
}
}
impl Drop for OwnedFoo {
fn drop(&mut self) { c_foo_free((self.0).0) }
}
impl Deref for OwnedFoo {
type Target = Foo;
fn deref(&self) -> &Self::Target { &self.0 }
}
impl DerefMut for OwnedFoo {
fn deref_mut(&mut self) -> &mut Self::Target { &mut self.0 }
}
struct Foo(*const RawFoo);
impl Foo {
fn count(&self) -> u8 { c_foo_count(self.0) }
fn make_awesome(&mut self, v: bool) { c_foo_make_awesome(self.0, v) }
}
fn main() {
let mut f = OwnedFoo::new();
println!("{}", f.count());
f.make_awesome(true);
}
Then, when you get a borrowed pointer from your other object, just wrap it up in a &Foo:
use std::mem;
fn c_bar_foo_ref() -> *const RawFoo { std::ptr::null() }
// Ignoring boilerplate for wrapping the raw Bar pointer
struct Bar;
impl Bar {
fn new() -> Bar { Bar }
fn foo(&self) -> &Foo {
unsafe { mem::transmute(c_bar_foo_ref()) }
}
fn foo_mut(&mut self) -> &mut Foo {
unsafe { mem::transmute(c_bar_foo_ref()) }
}
}
fn main() {
let mut b = Bar::new();
println!("{}", b.foo().count());
b.foo_mut().make_awesome(true);
// Doesn't work - lifetime constrained to Bar
// let nope = Bar::new().foo();
}

How to specify that method argument must have longer lifetime than self's lifetime?

I'd like to write a safe Rust wrapper for a C library. I need to express the C's library raw pointer ownership rules in Rust's terms.
The library has its private structure such as: struct handle {void *_data} and exposes setter as set_data(struct handle*, void *data).
I'd like to make Rust version of that method with signature that says "data must live at least as long as the handle".
I've tried:
set_data(&'a self, &'a data:…)
but borrow checker seems to apply that to lifetime within that function, not overall lifetime of the object.
I've also tried to add lifetime to impl, but that's still no good. Full test case:
#![allow(unused_variables)]
struct Handle<'a>;
impl<'a> Handle<'a> {
pub fn set_data(&'a mut self, data: &'a DropCanary) {
// save data raw ptr
}
pub fn use_data(&'a self) {
// use data raw ptr
println!("alive?");
}
}
fn main() {
let mut handle = Handle;
let long_enough_lifetime = DropCanary{label:"long"};
{
let short_lifetime = DropCanary{label:"short"};
handle.set_data(&short_lifetime); // This shouldn't be allowed!
handle.set_data(&long_enough_lifetime); // This is OK
}
handle.use_data();
}
/// --- just for testing ---
struct DropCanary {
label: &'static str,
}
impl Drop for DropCanary {
fn drop(&mut self) {
println!("dropped: {}", self.label);
}
}
The problem is that the following code compiles and outputs:
dropped: short
alive?
dropped: long
So it causes use-after-free, because Rust doesn't know that short_lifetime must outlive handle.
This should work for you (playpen).
The reason that your example compiles is simply because you do not use the lifetime inside the struct inside your example. Since you do not use it there is no constraint on it and it could just as well have been omitted. If you do not store any data with a lifetime inside your struct there is marker types which you can substitute for the Option I used here.
#![allow(unused_variables)]
struct Handle<'a>(Option<&'a DropCanary>);
impl<'a> Handle<'a> {
pub fn set_data(&mut self, data: &'a DropCanary) {
self.0 = Some(data);
// save data raw ptr
}
pub fn use_data(&self) {
// use data raw ptr
println!("alive?");
}
}
fn main() {
let mut handle = Handle(None);
let long_enough_lifetime = DropCanary{label:"long"};
{
let short_lifetime = DropCanary{label:"short"};
//handle.set_data(&short_lifetime); // This shouldn't be allowed!
handle.set_data(&long_enough_lifetime); // This is OK
}
handle.use_data();
}
/// --- just for testing ---
struct DropCanary {
label: &'static str,
}
impl Drop for DropCanary {
fn drop(&mut self) {
println!("dropped: {}", self.label);
}
}

error: `line` does not live long enough (but I know it does)

I am trying to make some kind of ffi to a library written in C, but got stuck. Here is a test case:
extern crate libc;
use libc::{c_void, size_t};
// this is C library api call
unsafe fn some_external_proc(_handler: *mut c_void, value: *const c_void,
value_len: size_t) {
println!("received: {:?}" , std::slice::from_raw_buf(
&(value as *const u8), value_len as usize));
}
// this is Rust wrapper for C library api
pub trait MemoryArea {
fn get_memory_area(&self) -> (*const u8, usize);
}
impl MemoryArea for u64 {
fn get_memory_area(&self) -> (*const u8, usize) {
(unsafe { std::mem::transmute(self) }, std::mem::size_of_val(self))
}
}
impl <'a> MemoryArea for &'a str {
fn get_memory_area(&self) -> (*const u8, usize) {
let bytes = self.as_bytes();
(bytes.as_ptr(), bytes.len())
}
}
#[allow(missing_copy_implementations)]
pub struct Handler<T> {
obj: *mut c_void,
}
impl <T> Handler<T> {
pub fn new() -> Handler<T> { Handler{obj: std::ptr::null_mut(),} }
pub fn invoke_external_proc(&mut self, value: T) where T: MemoryArea {
let (area, area_len) = value.get_memory_area();
unsafe {
some_external_proc(self.obj, area as *const c_void,
area_len as size_t)
};
}
}
// this is Rust wrapper user code
fn main() {
let mut handler_u64 = Handler::new();
let mut handler_str = Handler::new();
handler_u64.invoke_external_proc(1u64); // OK
handler_str.invoke_external_proc("Hello"); // also OK
loop {
match std::io::stdin().read_line() {
Ok(line) => {
let key =
line.trim_right_matches(|&: c: char| c.is_whitespace());
//// error: `line` does not live long enough
// handler_str.invoke_external_proc(key)
}
Err(std::io::IoError { kind: std::io::EndOfFile, .. }) => break ,
Err(error) => panic!("io error: {}" , error),
}
}
}
Rust playpen
I get "line does not live long enough" error if I uncomment line inside the loop. In fact, I realize that Rust is afraid that I could store short-living reference to a slice somewhere inside Handler object, but I quite sure that I wouldn't, and I also know, that it is safe to pass pointers to the external proc (actually, memory is immidiately copied at the C library side).
Is there any way for me to bypass this check?
The problem is that you are incorrectly parameterizing your struct, when you really want to do it for the function. When you create your current Handler, the struct will be specialized with a type that includes a lifetime. However, the lifetime of line is only for the block, so there can be no lifetime for Handler that lasts multiple loop iterations.
What you want is for the lifetime to be tied to the function call, not the life of the struct. As you noted, if you put the lifetime on the struct, then the struct is able to store references of that length. You don't need that, so put the generic type on the function instead:
impl Handler {
pub fn new() -> Handler { Handler{obj: std::ptr::null_mut(),} }
pub fn invoke_external_proc<T>(&mut self, value: T) where T: MemoryArea {
let (area, area_len) = value.get_memory_area();
unsafe {
some_external_proc(self.obj, area as *const c_void,
area_len as size_t)
};
}
}
Amended answer
Since you want to specialize the struct on a type, but don't care too much about the lifetime of the type, let's try this:
#[allow(missing_copy_implementations)]
pub struct Handler<T: ?Sized> {
obj: *mut c_void,
}
impl<T: ?Sized> Handler<T> {
pub fn new() -> Handler<T> { Handler{ obj: std::ptr::null_mut() } }
pub fn invoke_external_proc(&mut self, value: &T) where T: MemoryArea {
let (area, area_len) = value.get_memory_area();
unsafe {
some_external_proc(self.obj, area as *const c_void,
area_len as size_t)
};
}
}
Here, we allow the type to be unsized. Since you can't pass an unsized value as a parameter, we now have to take a reference instead. We also have to change the impl:
impl MemoryArea for str {
fn get_memory_area(&self) -> (*const u8, usize) {
let bytes = self.as_bytes();
(bytes.as_ptr(), bytes.len())
}
}

Resources