www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - preview in: it seems that return type auto solves scope variable

reply Dmytro Katyukha <firemage.dima gmail.com> writes:
Hi,

Not sure it is bug, or feature, or what is going on in examples 
below (with `-preview=in` enabled).

For example, we have following code snippet:

```d
import std.stdio;
import std.path;

struct Path {
     private string _path;

      safe pure nothrow this(in string path) {
         _path = path.dup;
     }

      safe pure nothrow this(in string[] segments...) {
         this(std.path.buildNormalizedPath(segments));
     }

     /// Check if current path is inside other path
      safe bool isInside(in Path other) const {
         import std.algorithm: startsWith;
         return this.toAbsolute.segments.startsWith(
             other.toAbsolute.segments);
     }

      safe Path toAbsolute() const {
         return Path(
             std.path.buildNormalizedPath(
                 std.path.absolutePath(_path.dup.expandTilde)));
     }

      safe pure auto segments() const {
         return std.path.pathSplitter(_path);
     }

}

unittest {
     assert(Path("my", "dir", "42").isInside(Path("my", "dir")) == 
true);
     assert(Path("my", "dir", "42").isInside(Path("oth", "dir")) 
== false);
}

void main()
{
     auto p = Path(".");
     auto h = Path("~");
     auto r = p.isInside(h);
}
```

Without `-preview=in` option, this code sample works just fine.
But when i enable `-preview=in` (or just manualy replace `in` 
with `scope const` in `isInside` method) it fails with following 
error:

```
source/app.d(19,13): Error: scope variable `other` assigned to 
non-scope parameter `this` calling `toAbsolute`
```

So, the first question is: where in this code assignment to 
`this` happens?

But, what is more strange, is that if i change return type for 
method `toAbsolute` to `auto`, error disappears:

```diff
-     safe Path toAbsolute() const {
+     safe auto toAbsolute() const {
```

So, the second question is: what happens in this case?

PS: What is the status of preview `in`? Do it have sense to take 
it into account?
Feb 09 2023
parent reply Dennis <dkorpel gmail.com> writes:
On Thursday, 9 February 2023 at 17:42:00 UTC, Dmytro Katyukha 
wrote:
 So, the first question is: where in this code assignment to 
 `this` happens?
When calling a member function like `other.toAbsolute`, you're assigning `other` to the implicit `this` parameter, as if you are calling `toAbsolute(this: other)`.
 So, the second question is: what happens in this case?
Functions with `auto` return get attributes inferred, just like template functions. The compiler infers `scope` based on the implementation of `toAbsolute`.
Feb 09 2023
parent reply Dmytro Katyukha <firemage.dima gmail.com> writes:
On Thursday, 9 February 2023 at 17:59:39 UTC, Dennis wrote:
 On Thursday, 9 February 2023 at 17:42:00 UTC, Dmytro Katyukha 
 wrote:
 So, the first question is: where in this code assignment to 
 `this` happens?
When calling a member function like `other.toAbsolute`, you're assigning `other` to the implicit `this` parameter, as if you are calling `toAbsolute(this: other)`.
 So, the second question is: what happens in this case?
Functions with `auto` return get attributes inferred, just like template functions. The compiler infers `scope` based on the implementation of `toAbsolute`.
Hi, thank you. Got it) So, the next questions: - Do it have sense to explicitly annotate all (most) member methods of a struct with `scope` attribute? Or it is better to use `auto` everywhere?. - What drawbacks of annotating all (most) parameters as scope? Is it correct approach? - What about `in` parameter attribute? Why it is not used wide in Phobos? How to correctly call method from standard lib (for example [expandTilde](https://dlang.org/phobos/std_path.html#expandTilde)) that does not annotated param with `scope` attribute? For example: ```d safe unittest { scope string p1 = "~/projects/d"; scope string p2 = expandTilde(p1); assert(p1 != p2); } ``` In this case, following error raised: ``` source/app.d(41,35): Error: scope variable `p1` assigned to non-scope parameter `inputPath` calling `expandTilde` ``` Is it correct, to use `.dup` method? ```diff safe unittest { scope string p1 = "~/projects/d"; - scope string p2 = expandTilde(p1); + scope string p2 = expandTilde(p1.dup); assert(p1 != p2); } ```
Feb 09 2023
parent tsbockman <thomas.bockman gmail.com> writes:
On Thursday, 9 February 2023 at 21:28:54 UTC, Dmytro Katyukha 
wrote:
 So, the next questions:
 - Do it have sense to explicitly annotate all (most) member 
 methods of a struct with `scope` attribute?
 ...
 - What drawbacks of annotating all (most) parameters as scope?
`scope` restricts what a function's implementation can do, while giving more freedom to the caller. (Mostly, this is the freedom to allocate less things with the GC.) This is generally a good thing, provided that you understand when `scope` and `return scope` apply well enough not to be slowed down or confused by them too much. Of course, sometimes you will actually have a good reason to escape a reference to a global or whatever, and `scope` doesn't do anything for types without indirections, so obviously you shouldn't put `scope` *everwhere*.
 Or it is better to use `auto` everywhere?.
Whether it is better to explicitly write out attributes and types, or have the compiler infer them via `auto` and/or templatization, depends on whether you want the compiler to give you an error message if you change the implementation in a way that alters the API, and how you value explicit documentation of the API versus keeping your code concise. I recommend being as explicit as practical for public APIs, and for any aspect of any API that is fundamental to its purpose. (For example, a function intended to be used in CTFE should be explicitly `pure`.) Just be careful not to over-constrain template APIs without a good reason.
 - What about `in` parameter attribute? Why it is not used wide 
 in Phobos?
Most of Phobos was written before the semantics of `in` or `scope` were finalized. That's why there is `-preview=in` and `-dip1000`, because them actually working is a somewhat new, work-in-progress feature.
 How to correctly call method from standard lib (for example 
 [expandTilde](https://dlang.org/phobos/std_path.html#expandTilde)) that does
not annotated param with `scope` attribute?
If there is no obvious valid reason for the function to be escaping one of its parameters, it's likely a bug in Phobos - a bit of code that hasn't been fully updated yet for `-dip1000`. This is especially likely to be the case if the parameter in question is not `immutable`.
 For example:

 ```d
  safe unittest {
     scope string p1 = "~/projects/d";
     scope string p2 = expandTilde(p1);
     assert(p1 != p2);
 }
 ```

 In this case, following error raised:

 ```
 source/app.d(41,35): Error: scope variable `p1` assigned to 
 non-scope parameter `inputPath` calling `expandTilde`

 ```

 Is it correct, to use `.dup` method?

 ```diff
   safe unittest {
      scope string p1 = "~/projects/d";
 -    scope string p2 = expandTilde(p1);
 +    scope string p2 = expandTilde(p1.dup);
      assert(p1 != p2);
  }
 ```
Either use `.dup`, `.idup`, or don't make `p1` `scope` in the first place. The last option is preferred if you know you would otherwise need to `.dup` the majority of the time. Also, the compiler is generally pretty good about inferring `scope` where needed for local stack variables, so `scope` should mostly only appear explicitly when receiving `scope` parameters, not when sending them.
Feb 09 2023