A question about (possible) drop semantics


#1

Hy there,

I have always wondered about a particularly aspect of the drop semantics, In the example from the recent introducing MIR Blog post the (pseudocodish) semantics of drop is shown in contexts where the value might be moved beforehand.

fn send_if(data: Vec<Data>) {
    let mut data_is_owned = true;

    if some_condition(&data) {
        send_to_other_thread(data);
        data_is_owned = false;
    }

    post_send();

    // Free `data`, but only if we still own it:
    if data_is_owned {
        mem::drop(data);
    }
}

So why wasn’t a semantic used witch specifies that “if the controll flow branches and a value is moved/droped in on branch, the moment the branches (possibly) come together again it assured to be dropped”

This sounds complex but (for me) would be kind of intuitive. The example from above would then be:

fn send_if(data: Vec<Data>) {

    if some_condition(&data) {
        send_to_other_thread(data);
    } else {
        mem::drop(data)
    }

    post_send();
}

This would also overlap with the analysis the borrow checker already does.

Note that in case of a return in a branch the graph would “join” again on the return e.g.:

fn send_if(data: Vec<Data>) {

    if some_condition(&data) {
       send_to_other_thread(data);
       return 
    }
    
    post_send();

    mem::drop(data)
    return 
}

Through I’m not sure if this would make a measurable difference in speed on a modern computer it might be preferable in more close to metal applications due to really having zero-overhead.

Also I can’t come up for a good reason against this pattern, but given that e.g. some unsafe+asyn code might depend on this semantics (and fail in a very subtile manner) changing to it probably a nogo and if you really want this behavior you could write all the “drop on join” statements per hand => all (in the future existing) flags will be optimized away (but it’s just to much to do per hand )

So why was the variation with slightly more overhead chosen or to rephrase witch point(s) did I miss? :wink:


#2

There’s a link in the MIR post to the non-zeroing drop RFC 320. This references two closed RFCs, static drop RFC 210 and eager drop RFC 239, which may shed some light on the history here.


#3

thanks :smile:

the static drop RFC 210, is very similar to the thinks I wrote above. To summarize on of the main reasons not to implement it was, that it can be quite unexpected when thinks are dropped, given that it might no longer align with the scope visually highlighted through the code brackets ({ ... }). Where this doesn’t make a different for most data for types with side effect on drop (e.g. Thread Guards…) this might lead to subtile bugs.

For eager drop this seems even more so to be the case :wink: