What ever happened to move semantics?

Timon Gehr timon.gehr at gmx.ch
Wed Feb 28 21:03:03 UTC 2024


On 2/28/24 21:38, Walter Bright wrote:
> On 2/28/2024 6:22 AM, Timon Gehr wrote:
>> On 2/28/24 15:17, Timon Gehr wrote:
>>> ...
>>> What is missing from the DIP?
>>> ...
>>>
>>
>> 19. Explicit moves.
>>
>> There is no way to force that a given occurrence of a variable is the 
>> last use and is moved. We can use std.algorithm.move, but the DIP as 
>> specified would just move a copy if something is used again.
> 
> Forcing a move just sounds like trouble. If it is not the last use, and 
> it is moved, then wouldn't the result be undefined behavior?

No, the idea is that the compiler enforces that it is indeed the last 
use and produces a compile-time error message if it cannot prove that it 
is the case.

> Determining 
> last use should be in the purview of the compiler, not the user, so it 
> is reliable.
> ...

Yes. It still holds that one may want to make sure that a value is 
really moved at a given point. Sometimes this matters. Anyway, this is 
by far not the most important point.


> The Ownership/Borrowing system does determine last use, using data flow 
> analysis.
> 
> 
>> What I would like to see is:
>>
>> ```d
>> void main(){
>>      S s;
>>      foo(move(s));
>>      auto t=s; // error, `s` has been moved
>>      S s; // ok, can redeclare `s`
>> }
>> ```
>>
>> Maybe there needs to be a parameter annotation that forces a move, 
>> then `move` can be implemented in terms of that.
> 
> What is the point of declaring another variable of the same name?

 From my previous post:

> 
> 16. Missing: Redeclaration after Move
> 
> ```d
> S s, t;
> func(s); // moved, `s` no longer accessible
> S s = t; // explicit construction via redeclaration
> ```
> 
> A nice feature of this is that the type of a variable can be changed on redeclaration. Note that Rust allows this.

This is a relatively common idiom in languages that support moves. It is 
annoying if you have to invent a new name for each intermediate result.

One use case would be type state:

File!(FileState.flushed) file = open("file.txt");
File!(FileState.buffered) file = file.write("hello ");
File!(FileState.buffered) file = file.writeln("world!");
// file.close(); // compile time error
File!(FileState.flushed) file = file.flush();
file.close(); // moves "file"

// file.write("hello"); // compile time error

Assume you have some declarations like these and you want to comment out 
part of the statements. It would now be annoying to have to rename 
variables.

I.e., you want to use the same name for different versions of the same 
thing, similarly to how you do not have to change the name of a variable 
when assigning to it.

> We already disallow shadowing declarations, and that has prevented a number 
> of bugs at least in my own code (they're very difficult to spot with a 
> visual check).

The reason why shadowing is error prone is that multiple variables with 
overlapping lifetimes are in scope and the compiler arbitrarily picks 
one of them. This case is different, as only one variable of the same 
name exists at any given time. This is not error prone. Requiring unique 
names is more error prone in this case, as you can accidentally copy an 
older version of a variable.

Anyway, this is not the most important thing, please do check out the 
points I initially included in my review. This point is just something I 
had forgotten to include.


More information about the Digitalmars-d mailing list