On Monday, 12 July 2021 at 23:45:57 UTC, someone wrote:
Regarding -preview=dip1000 (and the explicit error description
that could have helped me a lot back then) : DMD man page says
the preview switch lists upcoming language features, so DIP1000
is something like a D proposal as I glanced somewhere sometime
ago ... where do DIPs get listed (docs I mean) ?
DIPs are handled in this repository:
https://github.com/dlang/DIPs
This is a list of every DIP that is going through or has gone
through the review process:
https://github.com/dlang/DIPs/blob/master/DIPs/README.md
DIP1000 is here:
https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1000.md
But it doesn't describe the actual implementation, as described
here:
https://github.com/dlang/DIPs/blob/master/DIPs/other/DIP1000.md#addendum
I don't know what all the differences are, as I haven't followed
it.
So, every *local* variable within a chunk of code, say, a
function, should be declared without anything else to avoid
this type of behavior ? I mean, anything in code that it is not
private/public/etc.
Not "without anything", but without scope---unless you're using
-preview=dip1000, or unless you're applying it to class
references (see below).
Or, as I presume, every *local* meaning *aux* variable that
won't need to survive the function should be declared scope but
*not* the one we are returning ... lstrSequence in my specific
case ?
Can I declare everything *scope* within and on the last line
using lstrSequence.dup instead ? dup/idup duplicates the
variable (the first allowing mutability while the second not)
right ?
Which one of the following approaches do you consider best
practice if you were directed to explicitly state as much
behavior as possible ?
Consider this example, which demonstrates the original purpose of
scope prior to DIP 1000:
```d
import std.stdio;
class C {
int id;
this(int id) { this.id = id; }
~this() { writeln("C destructor #", id); }
}
struct S {
int id;
this(int id) { this.id = id; }
~this() { writeln("S destructor #", id); }
}
void main()
{
{
C c1 = new C(1);
scope c2 = new C(2);
S s1 = S(1);
S* s2 = new S(2);
scope s3 = new S(3);
writeln("The inner scope is exiting now.");
}
writeln("Main is exiting now.");
}
static ~this() { writeln("The GC will cleanup after this
point."); }
```
Classes are reference types and must be allocated. c1 is
allocated on the GC and lives beyond its scope. By applying the
scope attribute to c2, its destructor is forced to execute when
its scope exits. It is not allocated on the GC, but on the stack.
Structs are value types, so s1 is automatically allocated on the
stack. Its destructor will be always be called when the scope
exits. s2 is a pointer allocated on the GC heap, so its lifetime
is managed by the GC and it exists beyond its scope. s3 is also
of type S*. The scope attribute has no effect on it, and it is
still managed by the GC. If you want stack allocation and RAII
destructors for structs, you just use the default behavior like
s1.
You can run it here:
https://run.dlang.io/is/iu7QiO
Someone else will have to explain what DIP 1000 actually does
right now (if anyone really knows). What I'm certain about is
that it prevents things like this:
```d
void func() {
int i = 10;
int* pi = &i;
return pi;
```
The compiler has always raised an error when it encountered
something like `return &i`, but the above would slip by. With
-preview=dip1000, that is also an error. But scope isn't needed
on either variable for it to do so.
Beyond that, my knowledge of DIP 1000's implementation is
limited. But I do know that scope has no effect on variables with
no indirections. It's all about indirections (pointers &
references).
At any rate, DIP 1000 is not yet ready for prime time. Getting it
to that state is a current priority of the language maintainers.
So for now, you probably just shouldn't worry about scope at all.