Using same reference for multiple method parameters - rust

I'll preface by saying I'm very new to Rust, and I'm still wrapping my head around the semantics of the borrow-checker. I have some understanding of why it doesn't like my code, but I'm not sure how to resolve it in an idiomatic way.
I have a method in Rust which accepts 3 parameters with a signature that looks something like this:
fn do_something(&mut self, mem: &mut impl TraitA, bus: &mut impl TraitB, int_lines: &impl TraitC) -> ()
I also have a struct which implements all three of these traits; however, the borrow-checker is complaining when I attempt to use the same reference for multiple parameters:
cannot borrow `*self` as mutable more than once at a time
And also:
cannot borrow `*self` as immutable because it is also borrowed as mutable
My first question is whether this is a shortcoming of the borrow-checker (being unable to recognize that the same reference is being passed), or by design (I suspect this is the case, since from the perspective of the called method each reference is distinct and thus the ownership of each can be regarded separately).
My second question is what the idiomatic approach would be. The two solutions I see are:
a) Combining all three traits into one. While this is technically trivial given my library's design, it would make the code decidedly less clean since the three traits are used to interface with unrelated parts of the struct's state. Furthermore, since this is a library (the do_something method is part of a test), it hinders the possibility of separating the state out into separate structs.
b) Moving each respective part of the struct's state into separate structs, which are then owned by the main struct. This seems like the better option to me, especially since it does not require any changes to the library code itself.
Please let me know if I'm missing another solution, or if there's a way to convince the borrow-checker to accept my original design.

The borrow checker is operating as designed. It only knows you are passing three different mutable references into the same function: it does not know what the function will do with these, even if they do happen to be references to the same struct. Within the function they are three different mutable references to the same struct.
If the three different traits represent three different functional aspects, then your best approach might be to split the struct into different structs, each implementing one of the traits, as you have proposed.
If you would prefer to keep a single struct, and if the function will always be called with a single struct, then you can just pass it in once like this:
fn do_something(&mut self, proc: &mut (impl TraitA + TraitB + TraitC)) -> () { ... }

Related

Is it possible to borrow different parts of self with different mutability statically?

I am designing some struct with a prepare-commit pattern in rust, which mostly looks like this after simplified:
struct Data {
a: usize,
}
struct Adder<'a> {
data: &'a mut Data,
added: usize,
}
struct Holder<'a> {
data: &'a Data,
}
impl<'a> Adder<'a> {
fn add_once(&mut self) {
// Needs added to be mut, but not data
self.added = self.added + self.data.a;
}
fn commit(&mut self) {
// Needs data to be mut, but not added
self.data.a = self.added;
}
}
fn main() {
let mut d = Data { a: 1 };
let h = Holder { data: &d };
let mut ad = Adder {
data: &mut d,
added: 0,
};
ad.add_once();
ad.add_once();
println!("{}", h.data.a); // Cannot borrow here
ad.commit();
println!("{}", ad.data.a);
}
This snippet does not compile. The problem with the code snippet is that since Adder is borrowing data as mutable, upon the creation of Adder struct, all other immutable borrows (e.g.Holder here) could not live past. However, the add_once calls really do not need to borrow data mutably, only commit does that. Ideally there should be some way in rust to let the compiler know that add_once only needs added as mutable but not data, so that Holder could live past add_once calls but not commit call.
I do know two possible current solutions to this, however I think they both have drawbacks:
I could wrap fields of Adder in RefCell and borrow entire Adder immutably, only borrows mutably when needed. The problem with this approach is that RefCell checks borrowing dynamically, when in this case it really does not need to be that way since all lifetime bounds should be known at compile time, just different parts of a struct could have different bounds.
I could do unsafe blocks to convert references to raw pointers and do whatever I want. However as far as I think about it I could not find a way to do it without disable all borrow checks altogether (I don't really know how to do unsafe rust properly). And I want a borrow check so that if Holder lives past commit the compiler would still give a proper borrow error
So summary of questions:
Is there a static and safe way in rust to borrow different parts of self, with different mutability?
If not, is there a unsafe boilerplate to do so, still ensuring the proper borrow check as described above? (and if there is an existing crate for this)
What is the best practice in general for these situations?
Is there any RFC addressing this issue from rust language level?
Is there a static and safe way in rust to borrow different parts of self, with different mutability?
No.
If not, is there a unsafe boilerplate to do so, still ensuring the proper borrow check as described above? (and if there is an existing crate for this)
You can use unsafe for that, but it will require you to work with raw pointers only. You may be able to encapsulate it in a safe API, but it's unlikely that inside the code you'll have proper automatic verification. For this reason I also don't believe there is a crate for that, at least I don't know one.
What is the best practice in general for these situations?
It depends.
I would use RefCell (or other interior mutability primitives such as Cell - note that if you can use Cell it is actually zero-cost) for that generally, and use unsafe code if profiling shows a bottleneck. However, this kind of issues many times arises from improper API design. So first I'll see if I can redesign my API so the problem will not exist.
Is there any RFC addressing this issue from rust language level?
Not today, and I think it is very unlikely in the future.
There was something similar suggested while working on two phase borrows: https://github.com/rust-lang/rust/issues/49434. The idea was that any mutable reference will start as shared and only "upgrade" when it is actually used. In can be extended to "upgrade" only when a mutable reference is actually required, and this is basically what you propose.
However, the effect of two-phase borrows is completely local. For your code to work, we will have to need some way to represent time-aware mutable reference in a type. I don't see that happening.
In addition, two-phase borrows work poorly with Stacked Borrows. #RalfJung ended up with just using raw pointers when two phase borrows are involved. Using them for everything will probably mean poor optimization opportunities (although maybe future models, or even Stacked Borrows itself, will find a way to handle that).

Is my understanding of a Rust vector that supports Rc or Box wrapped types correct?

I'm not looking for code samples. I want to state my understanding of Box vs. Rc and have you tell me if my understanding is right or wrong.
Let's say I have some trait ChattyAnimal and a struct Cat that implements this trait, e.g.
pub trait ChattyAnimal {
fn make_sound(&self);
}
pub struct Cat {
pub name: String,
pub sound: String
}
impl ChattyAnimal for Cat {
fn make_sound(&self) {
println!("Meow!");
}
}
Now let's say I have other structs (Dog, Cow, Chicken, ...) that also implement the ChattyAnimal trait, and let's say I want to store all of these in the same vector.
So step 1 is I would have to use a Box type, because the Rust compiler cannot determine the size of everything that might implement this trait. And therefore, we must store these items on the heap – viola using a Box type, which is like a smarter pointer in C++. Anything wrapped with Box is automatically deleted by Rust when it goes out of scope.
// I can alias and use my Box type that wraps the trait like this:
pub type BoxyChattyAnimal = Box<dyn ChattyAnimal>;
// and then I can use my type alias, i.e.
pub struct Container {
animals: Vec<BoxyChattyAnimal>
}
Meanwhile, with Box, Rust's borrow checker requires changing when I pass or reassign the instance. But if I actually want to have multiple references to the same underlying instance, I have to use Rc. And so to have a vector of ChattyAnimal instances where each instance can have multiple references, I would need to do:
pub type RcChattyAnimal = Rc<dyn ChattyAnimal>;
pub struct Container {
animals: Vec<RcChattyAnimal>
}
One important take away from this is that if I want to have a vector of some trait type, I need to explicitly set that vector's type to a Box or Rc that wraps my trait. And so the Rust language designers force us to think about this in advance so that a Box or Rc cannot (at least not easily or accidentally) end up in the same vector.
This feels like a very and well thought design – helping prevent me from introducing bugs in my code. Is my understanding as stated above correct?
Yes, all this is correct.
There's a second reason for this design: it allows the compiler to verify that the operations you're performing on the vector elements are using memory in a safe way, relative to how they're stored.
For example, if you had a method on ChattyAnimal that mutates the animal (i.e. takes a &mut self argument), you could call that method on elements of a Vec<Box<dyn ChattyAnimal>> as long as you had a mutable reference to the vector; the Rust compiler would know that there could only be one reference to the ChattyAnimal in question (because the only reference is inside the Box, which is inside the Vec, and you have a mutable reference to the Vec so there can't be any other references to it). If you tried to write the same code with a Vec<Rc<dyn ChattyAnimal>>, the compiler would complain; it wouldn't be able to completely eliminate the possibility that your code might be mutating the animal at the same time as the code that called it was in the middle of trying to read the animal, which might lead to some inconsistencies in the calling code.
As a consequence, the compiler needs to know that all the elements of the Vec have their memory treated in the same way, so that it can check to make sure that a reference to some arbitrary element of the Vec is being used appropriately.
(There's a third reason, too, which is performance; because the compiler knows that this is a "vector of Boxes" or "vector of Rcs", it can generate code that assumes a particular storage mechanism. For example, if you have a vector of Rcs, and clone one of the elements, the machine code that the compiler generates will work simply by going to the memory address listed in the vector and adding 1 to the reference count stored there – there's no need for any extra levels of indirection. If the vector were allowed to mix different allocation schemes, the generated code would have to be a lot more complex, because it wouldn't be able to assume things like "there is a reference count", and would instead need to (at runtime) find the appropriate piece of code for dealing with the memory allocation scheme in use, and then run it; that would be much slower.)

Can you specify return type mutability in Rust?

Is it possible to specify the mutability of the assigned variable in rust? Something like
fn new(len: usize) -> Thing { ... }
fn new_mut(len: usize) -> mut Thing { ... }
I have a specific case where knowing the mutability of a type can be used to make several optimizations under the hood for my data structure.
Trying to enforce mutability manually is possible, but seems quite inelegant, especially when the concept of mutability is an intrinsic part of the rust language already. And you end up in weird situations like this:
// Thing::new() returns a data structure with an immutable backing type,
// but the code below looks like it should be mutable.
let mut foo = Thing::new(5);
In this case, I either have to choice of trying to figure out if someone tried to make a mutable reference to my immutable Thing manually (and panicking I suppose), or by making new return a wrapper over Thing that hides all mutable functions (which means the mut keyword is rendered pointless and misleading).
I think you have some misconception: mutability of a return type is not and should NOT be part of the function signature, mutability is always decided on the caller side.
The return type is a description of the memory slot returned to the caller when the call stack returns after executing the function. Ownership of the returned type is fully transferred, e.g. a Thing is fully moved to the caller. How the caller handles the returned memory cell is not the concern of the called function because it's already finished and returned. There's no such thing as mutable or immutable return type, mutability is always related to memory slots. In your example this is only decided at declaring variable foo that defines the memory slot of the result type on the caller side. As long as you have full ownership of a data structure, you're free to decide or even change mutability of the data.
What you're looking for is maybe a separate type specialized for optimization.

Lifetime of function reference in Rust

So I'm trying to squeeze in callbacks to this enum variant (Visual) - which will be stored in a vector as seen within the struct EntityComponents:
enum Component {
Position([f64; 2]),
Visual(& Fn(Entity) -> ()),
}
struct EntityComponents {
components_of_entity: HashMap<TypeId, Vec<Component>>,
}
However, Rust requires me to provide explicit lifetime parameters here.
My idea is that I want the function reference to live at least as long as its argument (the Entity), but I have no idea how the syntax for that would look like? Is it even possible?
The idea is that as long as an Entity has a Visual component, we can use this callback to render it!
A few things:
You used &Fn() instead of fn(). The former is a trait object of the Fn trait, the latter is a good ol' function pointer. While the former is more general (in particular, it supports closures), it's rather unusual to store a reference to a closure that lives somewhere else.
Therefore, if you want to store closures as well, you'd probably use a Box<Fn()> which solves your lifetime problems.
If you don't care about closures, but just want to save function pointer, you can simply write Visual(fn(Entity)),. Function pointer always have a static lifetime. So this solves your lifetime problems as well.
But what if you really want to store a reference to an Fn trait object, which lives somewhere else? The solution depends on where it lives:
In Entity: see here
somehere else: use a lifetime <'a>
You wrote Fn(Entity) -> (). The -> () is always useless, you can omit it.

How to idiomatically require a trait to retrieve a slice

I'm working on a library that operates on [T] slices. I would like the library user to specify a type that can retrieve a slice in a mutable or immutable way.
My current lib defines 2 traits
pub trait SliceWrapper<T> {
fn slice(&self) -> &[T];
}
pub trait SliceWrapperMut<T> {
fn slice_mut (&mut self) -> &mut [T];
}
But the names slice and slice_mut seem arbitrary and not somewhere in the core Rust libs.
Is there a trait I should be requiring instead, like Into::<&mut [T]> ?
I'm afraid that Into consumes the type rather than just referencing it, so I can't simply demand the caller type implements core::convert::Into, can I?
The simplest way to answer this is to look at the documentation for existing types that more or less do what you want. This sounds like something Vec would do, so why not look at the documentation for Vec?
If you search through it looking for -> &, you'll find numerous methods that return borrowed slices:
Vec::as_slice
Borrow::borrow
Index::<Range<usize>>::index (plus for RangeTo<usize>, RangeFrom<usize>, RangeFull, RangeInclusive<usize>, and RangeToInclusive<usize>)
Deref::deref
AsRef::<[T]>::as_ref
Which of these should you implement? I don't know; which ones make sense? Read the documentation on each method (and the associated trait) and see if what it describes is what you want to permit. You say "that can retrieve a slice", but you don't really explain what that means. A big part of traits is not just abstracting out common interfaces, but giving those interfaces meaning beyond what the code strictly allows.
If the methods listed aren't right; if none of them quite convey the correct semantics, then make a new trait. Don't feel compelled to implement a trait just because you technically can.
As for Into, again, read the documentation:
A conversion that consumes self, which may or may not be expensive.
Emphasis mine. Implementing Into in this context makes no sense: if you consume the value, you can't borrow from it. Oh, also:
Library authors should not directly implement this trait, but should prefer implementing the From trait, which offers greater flexibility and provides an equivalent Into implementation for free, thanks to a blanket implementation in the standard library.
So yeah, I wouldn't use Into for this. Or From.

Resources