So lets disabuse your mistrust of immutability in another domain!
Here is some typical "go fast and mutable!" nonsense code:
int foo(int i, int j) {
while (i < 10) {
j += i;
i++;
}
return j;
}
Let's compile it with https://godbolt.org/, turn on some optimisations and inspect the IR (-O2 -emit-llvm). Copying out the part that corresponds to the while loop: 4:
%5 = sub i32 9, %0, !dbg !20
%6 = add nsw i32 %0, 1, !dbg !20
%7 = mul i32 %5, %6, !dbg !20
%8 = zext i32 %5 to i33, !dbg !20
%9 = sub i32 8, %0, !dbg !20
%10 = zext i32 %9 to i33, !dbg !20
%11 = mul i33 %8, %10, !dbg !20
%12 = lshr i33 %11, 1, !dbg !20
%13 = trunc i33 %12 to i32, !dbg !20
tail call void @llvm.dbg.value(metadata i32 poison, metadata !17, metadata !DIExpression()), !dbg !18
tail call void @llvm.dbg.value(metadata i32 poison, metadata !16, metadata !DIExpression()), !dbg !18
%14 = add i32 %1, %0, !dbg !20
%15 = add i32 %14, %7, !dbg !20
%16 = add i32 %15, %13, !dbg !20
br label %17, !dbg !21
17:
%18 = phi i32 [ %1, %2 ], [ %16, %4 ]
Well, would you look at that! Clang decided (even in this hot loop) never to re-assign any of the left-hand-sides, even though my instructions were just: "mutate j in-place. mutate i in-place."