Methods borrow more than what they need

Recently started using rust and created this code for myself which demonstrated what I find a bit confusing. Wanted to share this to hear opinions.
I ran into this problem multiple times already and every time had to re-design the structs to control accessibility of different parts of the struct. Below is a simplified example of what was happening:

[Try it in rust Rust Playground)

struct X{
    a: u16,
    b: u16,
}
impl X{
   #[allow(dead_code)]
    fn a_mut(&mut self)  -> &mut u16{
        &mut self.a
    }
}

fn main() {
    let mut x : X = X{a:20,b:10};
    let b  = &x.b;
    
    // comment the following line and uncomment the line after the next one
    let a : &mut u16  = &mut x.a;     // borrows x.a -- compiles
    //let a : &mut u16  = x.a_mut();  // borrows x -- does not compile as x.b is borrowed already
    
    *a = 100;
    println!("A:{} B:{}",&a, &b);
}

It works due to https://doc.rust-lang.org/nomicon/borrow-splitting.html .

You might also see

3 Likes

Why do you find that confusing? It may be annoying, but it's definitely not confusing.

Let's simplify it to something that someone who doesn't know you, personally, would see:

struct X{
    a: u16,
    b: u16,
}
impl X{
    fn a_mut(&mut self)  -> &mut u16;
}

That's what the documentation for the crate would show, right? And why would said function be able to partially borrow a? Because it's called a_mut? That's not something compiler should look on.

As others have noted people have been thinking about about how to allow partial borrows, but the question is always whether additional complexity added to the language is warranted compared to how much it would simplify certain APIs.

2 Likes

It works when accessing the field directly, but it does not work with the accessor method

Sure understand that the interface does not say anything about what part of the struct is used by the implementation.
Meanwhile it is still confusing that to return a member by a method you need to borrow the whole thing. I did not think long enough to propose a solution, but from the usage perspective it's something you run into and need some time to understand. And this is only very simplified example. In more complex cases it's not even easy to figure out what's going on and after figuring out it is not obvious how to work that around.

Maybe a solution could be if we could have the borrowing a more explicit part of the method contract,
so that I could specify that a function argument is reference to the specified type but the implementation is not allowed to use the whole type but only the specified part of the type. This could solve many issues I think.

fn foo(ref : &Bar /*somehow restricted to borrow only Bar::{,y,z,x} */

I guess you didn't read @vague's second and third links?

You are right, I didn't. Thanks for pointing out. Will do.

This would be less likely to offend if you say it is not confusing to you. Otherwise it sounds like you're telling the other person they should not or cannot be confused by it, which is not a good idea of course when someone is asking for help.

1 Like

Thanks for sharing these links. Especially the Partial borrowing discussion.
Unfortunately since 2015 nothing was done with this.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.