A D vs. Rust example

Don Allen donaldcallen at gmail.com
Sat Oct 22 20:34:27 UTC 2022


On Saturday, 22 October 2022 at 05:35:22 UTC, victoroak wrote:
> On Thursday, 20 October 2022 at 13:37:07 UTC, Don Allen wrote:
>>
>> ````
>> fn main() {
>>     let mut foo = 5;
>>     let mut bar = || {
>>         foo = 17;
>>     };
>>     let mut baz = || {
>>         foo = 42;
>>     };
>>     bar();
>>     println!("{}", &mut foo);
>>     baz();
>>     println!("{}", &mut foo);
>>
>> }
>> ````
>>
>
> I think the way it would be advised to write it in Rust would 
> be something like this instead of using RefCell:
> ```
> fn main() {
>     struct State {
>         foo: i32
>     }
>
>     impl State {
>         fn bar(&mut self) {
>             self.foo = 17;
>         }
>
>         fn baz(&mut self) {
>             self.foo = 42;
>         }
>     }
>
>     let mut state = State {
>         foo: 0
>     };
>
>     state.bar();
>     println!("{}", state.foo);
>     state.baz();
>     println!("{}", state.foo);
> }
> ```
> The problem is that both closures have a mutable reference to 
> the same value and this is not allowed in Rust, you could solve 
> this making the functions get a mutable reference to the 
> variable but using a struct in this case is better IMO.

I think it's clear that my Scheme-ish attempt to use Rust 
closures was not a good idea. The fundamental problem with using 
Rust closures in this way is that the borrow-checker thinks  any 
mutable borrows that occur in the closure happen at the time the 
closure is defined, not when it is called. This is one of those 
situations where trying to insure safety at compile time can be 
too conservative, which the Rust folks concede.

Interior mutability and reference counting are both run-time ways 
to get around the compiler when it prevents you from doing things 
that are actually safe but can't be proven safe at compile time.

I never actually fixed the code from which my example is drawn, 
which I wrote quite awhile ago, because at that point I'd reached 
my personal Rust-pain threshold and moved on to D, returning my 
blood pressure to normal. But if I were going to fix it, I agree 
with you that your (very nice) solution is preferable. Avoiding 
closures and instead using functions/methods results in the 
mutable borrows occurring at call time, which solves the problem, 
since my call pattern doesn't violate the "one mutable borrow at 
a time" rule. I'm sure Refcell would also work, as it did with my 
little example, but it's messier and introduces a bit of runtime 
overhead (which I doubt would matter in the application in 
question running on 4 GHz hardware, so I think the readability 
issue is primary).

I'd also add that in effect, what you are doing is manually 
creating closures by using a struct and struct methods (behind 
the scenes, Rust closures are built by the compiler with structs 
in much the same way). But by doing it manually, you get finer 
grained control over how and when the mutable borrows occur, so 
they happen in a smarter way than what the compiler is doing.


More information about the Digitalmars-d mailing list