Struct that destroys its original handle on copy-by-value

John Colvin via Digitalmars-d-learn digitalmars-d-learn at puremagic.com
Sat Aug 1 10:50:26 PDT 2015


On Saturday, 1 August 2015 at 12:10:43 UTC, Joseph Rushton 
Wakeling wrote:
> On 31/07/15 19:21, Ali Çehreli via Digitalmars-d-learn wrote:
>> On 07/26/2015 04:29 AM, Joseph Rushton Wakeling via 
>> Digitalmars-d-learn wrote:
>>
>>  > is this design idea even feasible in principle, or just a 
>> bad
>>  > idea from the get-go?
>>
>> As I understand it, it is against one of fundamental D 
>> principles: structs are
>> value types where any copy can be used in place of any other.
>>
>> I expect there are examples where even Phobos violates it but 
>> the struct
>> documentation still says so: "A struct is defined to not have 
>> an identity; that
>> is, the implementation is free to make bit copies of the 
>> struct as convenient."
>>
>>    http://dlang.org/struct.html
>
> That really feels very bad for the problem domain I have in 
> mind -- random number generation.  No implementation should be 
> free to make copies of a random number generator "as 
> convenient", that should be 100% in the hands of the programmer!
>
>
>>  > And if feasible -- how would I go about it?
>>
>> Disallowing automatic copying and providing a function comes 
>> to mind.
>
> Yes, I considered that, but I don't think it really delivers 
> what's needed :-(
>
> Let me give a concrete example of why I was thinking in this 
> direction. Consider RandomSample in std.random.  This is a 
> struct (a value type, instantiated on the stack).  However, it 
> also wraps a random number generator. It needs to be consumed 
> once and once only, because otherwise there will be unintended 
> statistical correlations in the program.  Copy-by-value leads 
> to a situation where you can accidentally consume the same 
> sequence twice (or possibly, only _part_ of the sequence).
>
> Now, indeed, one way is to just @disable this(this) which 
> prevents copy-by-value.  But then you can't do something 
> natural and desirable like:
>
>     iota(100).randomSample(10, gen).take(5).writeln;
>
> ... because you would no longer be able to pass the 
> RandomSample instance into `take`.
>
> On the other hand, what you want to disallow is this:
>
>    auto sample = iota(100).randomSample(10, gen);
>
>    sample.take(5).writeln;
>    sample.take(5).writeln;   // statistical correlations result,
>                              // probably unwanted
>
> The first situation is still possible, and the second 
> disallowed (or at least, guarded against), _if_ a copy-by-value 
> is finalized by tweaking the source to render it an empty range.
>
> I would happily hear alternative solutions to the problem, but 
> that's why I was interested in a struct with the properties I 
> outlined in my original post.

Naïve compromise solution?

struct S(bool noCopy = true)
{
     //replace with real state
     int state = 0;
     static if(noCopy) @disable this(this);
     @property auto copyable()
     {
         //did the move manually because I got
         //weird results std.algorithm.move
         auto ret = cast(S!false)this;
         this.state = this.state.init;
         return ret;
     }
}

auto s(int state)
{
     return S!()(state);
}

void main()
{
     import std.stdio, std.algorithm;
     auto s = s(42);
     auto s1 = s.move;
     assert(s == S!().init);
     s1.copyable.writeln;
     assert(s1 == S!().init);
}

Then at least the simplest mistakes are avoided. Also, it means 
people are more likely to read important docs i.e. "Why do I have 
to call this copyable thing? Oh, I see, I'll be careful."

I'm not sure how good an idea it is to totally enforce a range to 
be non-copyable, even if you could deal with the function call 
chain problem. Even in totally save-aware code, there can still 
be valid assignment of a range type. I'm pretty sure a lot of 
phobos ranges/algorithms would be unusable.


More information about the Digitalmars-d-learn mailing list