sunface / rust-by-practice

Learning Rust By Practice, narrowing the gap between beginner and skilled-dev through challenging examples, exercises and projects.

Home Page:https://practice.rs

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

fight-compiler - alternative approaches?

nneonneo opened this issue · comments

The single "fight-compiler" exercise borrows the list for iteration, then attempts to borrow the structure mutably in each iteration, which naturally fails.

One of the reasons why this fails is that do_something receives a reference to the entire structure, meaning that it could (in principle) mutate the list while the iteration is ongoing in run. Obviously, Rust forbids this.

However, the proposed solution (https://github.com/sunface/rust-by-practice/blob/master/solutions/fight-compiler/borrowing.md) involves refactoring the iteration to a regular indexed loop. This doesn't actually solve the underlying (potential) safety hazard: do_something could still mutate the list, which could cause the caller to panic unexpectedly if e.g. the list was truncated by some other implementation of do_something. It can be argued that the API is broken here, because it does not appropriately restrict implementers from doing unexpected things.

There are a few ways this can be solved (IMHO) more cleanly, although they involve changing the API of do_something:

  • Don't give do_something access to &mut self; instead, give it a &mut i32 reference so it is only capable of mutating a and not list. So, pub fn do_something(a: &mut i32, n: i32) { *a = n; }
  • Convert do_something to a closure. Closures get special treatment because the compiler can directly see what they borrow. So, for example, self.list.iter().for_each(|i| { self.a = *i; }); works. In fact, we can even do something like self.list.iter().for_each(|i| { self.a = self.list[(7 - *i) as usize]; });, borrowing list immutably and a mutably, which (AFAIK) you simply cannot do with a separate function.
commented

Couldn't you also just clone the array before iterating it? I feel like the rules are a little too loose. :)