Partially borrowed structs && Unergonomic lifetimes


This is a thing I already discussed in IRC, and I got some really helpful replies. However, I’d like to discuss it a bit further here. I pasted the IRC log below for the reference.

I have a method, current_mut_player(&mut self) -> &mut Player, that returns a mut reference to either of two fields (of type Player) of the struct (of type GameState) passed to it. Please check code in the playpen:

The problem is that the reference to the struct originally passed to the method and the reference to a field the method returns have same the lifetime – that’s why it keeps the whole GameState struct borrowed although I need only mutable references to the specific fields. Now if I need to access some other fields, I have to drop the borrow first – although we can clearly statically see that the code is accessing different fields and would be, thus, safe anyway.

I was said that this is because the lifetime elision system uses only the function signatures to do the trick, so it doesn’t know that the code inside the function is going to disregard all the other fields.

I keep pondering, wouldn’t it be possible to make the borrow/lifetime checker to comply with this kind of case? Either automatically or with manual hints? Why isn’t it implemented yet, are there some disadvantages or odd corner cases to it? Are such features planned or suggested?

21:20 < drasa> <- Hi, I’m having a problem with the borrow checker. Could somebody help? Apparently Rust doesn’t have concept of “partially” borrowing structs…
21:20 < drasa> Basically, if I borrow one field mutably and do that inside of an function, it considers the whole struct borrowed
21:20 < Sharp> drasa: Yeah, there’s a way of working around that.
21:21 < XMPPwocky> drasa: no, it has a concept of partial borrows, but not across function boundaries.
21:21 < XMPPwocky> drasa: current_mut_player takes &mut self, and Rust doesn’t know that it doesn’t touch the board
21:21 < XMPPwocky> drasa: the borrow/region/type checkers only look at function signatures
21:22 < Sharp> drasa: Actually, yeah, with this API it won’t quite work
21:39 < Sharp> drasa: Anyway, the way you can work around that in general is, have a function that takes the fields you need explicitly, and only borrows the fields it really needs to borrow for a long time.
21:43 < drasa> Sharp: I see. I was wishing that there would be some kind of means to tell to the compiler that the function doesn’t keep borrowing other than the player fields of the struct
21:43 < drasa> It’s not a problem per se, but the API I’m making won’t be as clean :smiley:
21:43 < Sharp> drasa: Yeah, this was brought up yesterday… the issue is that Rust makes it really unergonomic to use free functions
21:43 < Sharp> drasa: So people tend to use methods for everything
21:44 < Sharp> drasa: But for a method to work like you say, it has to expose its internals to the user
21:44 < Sharp> drasa: It’s not clear whether the disadvantages there outweigh the advantages, it might change.
21:45 < drasa> Actually the current_mut_player is a method
21:45 < Sharp> drasa: Yeah I know, that’s what I mean
21:45 < drasa> So I don’t quite get what you mean with free functions
21:45 < Sharp> drasa: It’s a method, but it doesn’t need to borrow the entire struct.
21:45 < drasa> Ah
21:45 < Sharp> drasa: If it were easier to use free functions, you would just make a free function there, but in Rust you would never do that unless the borrow checker forced you to (basically)
21:46 < Sharp> drasa: So IMO that’s where issues like this come from.