Reference from function return VS manual reference

Hi,

I am very confused about something not compiling in my code right now.
I've made a seperate test project to test the behavior, and I get the same results.

test1 and test2 perform essentially the same thing, but test2 won't compile
It complains about:
cannot borrow a.v2 as mutable because it is also borrowed as immutable

Am I missing something here?

Thanks,
Jonathan

struct A {
	v1: u32,
	v2: u32,
}

impl A {
	fn get_v1_ref(&self) -> &u32 {
		&self.v1
	}
}



fn test(v1: &u32, v2: &mut u32) {
	println!("{},{}", v1, v2);
}


fn test1() {
	let mut a = A {
		v1: 12,
		v2: 14,
	};

	test(&a.v1, &mut a.v2);
}

fn test2() {
	let mut a = A {
		v1: 12,
		v2: 14,
	};

	test(a.get_v1_ref(), &mut a.v2);
}


fn main()
{
	test1();
	test2();
}

Rust doesn't do any global analysis. This includes peeking into other function bodies to see if something compiles. Looking at the signature of get_v1_ref: fn(&A) -> &u32, which binds the lifetime of &u32 to the &A. The entire &A. This later conflicts with the borrow to v2 later. If you [manually] inline get_v1_ref, Rust can see through the disjoint fields.

Note this [no global analysis] has the nice benefit that you can later change the body of the function without fear of breaking downstream code!

Oh ok that makes sense.
Thanks for a quick and great response!

I tried inlining with #[inline(always)] but it didn't work though.

Thanks,
Jonathan

Oh, I meant manually inlining, sorry for the confusion! :sweat_smile:

Oh so you mean just typing what the function does each time I need it?
Yeah that's what I resorted to doing, but it's kinda sad

Normally I avoid relying on getters (and setters if you have those). It usually don't end well, for exatly this reason. Why are you using getters?

I often need access to the last element of a vector (kind of a stack)
so the function does basically:

self.some_vec.last().unwrap()

it's not much, but it gets annoying to write every time.
Sometimes it's a bit longer though

In that case you could make a free function

fn last<T>(slice: &[T]) -> &T {
    slice.last().unwrap()
}
// And, use it like
(last(&self.my_vec), &mut self.my_mut)

This will allow Rust to reason about the disjointness of fields without adding too much visual overhead.
In the normal case you could use the getter, but in special cases you can use this free function
This idea generalizes, pass all the fields that are needed for a computation as parameters to a free function, so that Rust can reason about disjointness.
If you find certain fields are usually used together in these feee functions, that's a candidate for a new type

1 Like

Oh that's a good idea.
The most complicated getter functions used a series of iterators like .filter.map.find etc..
Having them in a free function would probly solve that.
My C++ brain never would have thought about that haha

Thanks!

1 Like