www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - ref struct member function

reply PinDPlugga <a a.com> writes:
Hi I am working through Programming in D. In one exercise I have 
a helper function in a struct

```D
struct Fraction {
     auto n = 0L;
     auto d = 1L;

     // ... other bits ...

     ref Fraction reduce() {
       import std.numeric : gcd;

       v = gcd(n, d);
       if (v > 1) {
         n /= v;
         d /= v;
       }

       return this;
     }

     // ... etc ...
}

void main() {
   import std.stdio : writeln;
   auto f = Fraction(3, 9);
   writeln(f.reduce());
}
```

This works but issues a deprecation warning:
```
onlineapp.d(30): Deprecation: returning `this` escapes a 
reference to parameter `this`
onlineapp.d(30):        perhaps annotate the parameter with 
`return`
Fraction(1, 3)
```

I found several other ways to make this work without the 
warnings, but I am wondering how to make this particular case 
work, or if it is not a recommended way, what the proper way 
would be.
May 13
parent reply Steven Schveighoffer <schveiguy gmail.com> writes:
On 5/13/21 3:21 PM, PinDPlugga wrote:

 This works but issues a deprecation warning:
 ```
 onlineapp.d(30): Deprecation: returning `this` escapes a reference to 
 parameter `this`
 onlineapp.d(30):        perhaps annotate the parameter with `return`
 Fraction(1, 3)
 ```
 
 I found several other ways to make this work without the warnings, but I 
 am wondering how to make this particular case work, or if it is not a 
 recommended way, what the proper way would be.
Just follow the recommendation: ```d ref Fraction reduce() return { // annotated with return ``` This means, "I will return all or part of the parameter passed in" (the parameter being the `this` reference) -Steve
May 13
parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 5/13/21 12:40 PM, Steven Schveighoffer wrote:
 On 5/13/21 3:21 PM, PinDPlugga wrote:
=20
 This works but issues a deprecation warning:
 ```
 onlineapp.d(30): Deprecation: returning `this` escapes a reference to =
 parameter `this`
 onlineapp.d(30):=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0=C2=A0 perhaps ann=
otate the parameter with `return`
 Fraction(1, 3)
 ```

 I found several other ways to make this work without the warnings, but=
=20
 I am wondering how to make this particular case work, or if it is not =
 a recommended way, what the proper way would be.
=20 Just follow the recommendation: =20 ```d ref Fraction reduce() return { // annotated with return ``` =20 This means, "I will return all or part of the parameter passed in" (the=
=20
 parameter being the `this` reference)
=20
 -Steve
I was writing this example that shows a use case for the problem (marked = with BUG below): struct Fraction { auto n =3D 0L; auto d =3D 1L; // ... other bits ... ref Fraction reduce() { import std.numeric : gcd; // v =3D gcd(n, d); // if (v > 1) { // n /=3D v; // d /=3D v; // } return this; } // ... etc ... } ref foo() { import std.stdio : writeln; auto f =3D Fraction(3, 9); // BUG: Returns reference to an object on the stack return f.reduce(); } void main() { // Refers to a temporary (it would be a copy without the &) auto f =3D &foo(); // Do some unrelated things hoping that space for dead objects on // the stack will be reused. import std.stdio; import std.random; writeln("doing something else: ", uniform(0, 6)); writeln(f.d); // Access dead and overwritten object (does NOT print // 9 on my system) } Putting 'return' where Steve shows makes the compiler guard us against=20 this misuse and the program thankfully does not compile. Ali
May 13
parent reply PinDPlugga <a a.com> writes:
On Thursday, 13 May 2021 at 19:48:44 UTC, Ali Çehreli wrote:
 I was writing this example that shows a use case for the 
 problem (marked with BUG below):
 ```D
 struct Fraction {
     auto n = 0L;
     auto d = 1L;

     // ... other bits ...

     ref Fraction reduce() {
       import std.numeric : gcd;

       // v = gcd(n, d);
       // if (v > 1) {
       //   n /= v;
       //   d /= v;
       // }

       return this;
     }

     // ... etc ...
 }

 ref foo() {
   import std.stdio : writeln;
   auto f = Fraction(3, 9);

   // BUG: Returns reference to an object on the stack
   return f.reduce();
 }

 void main() {
   // Refers to a temporary (it would be a copy without the &)
   auto f = &foo();

   // Do some unrelated things hoping that space for dead 
 objects on
   // the stack will be reused.
   import std.stdio;
   import std.random;
   writeln("doing something else: ", uniform(0, 6));

   writeln(f.d); // Access dead and overwritten object (does NOT 
 print
                 // 9 on my system)
 }
 ```
 Putting 'return' where Steve shows makes the compiler guard us 
 against this misuse and the program thankfully does not compile.

 Ali
Hi thank you both for your answers. I had understood from an earlier chapter how this could introduce a bug, but I was confused because the warning suggests attaching ```return``` to the parameter, which is empty in the declaration. So my next question would be how come ref based operator overloads are not labelled as return and do not show this warning? ``` D struct Fraction { auto n = 0L; auto d = 1L; ref Fraction opUnary(string op)() if (op == "++") { n += d; return this; } } ref Fraction foo() { auto f = Fraction(1, 3); // Same bug as with reduce return ++f; } ```
May 14
next sibling parent ag0aep6g <anonymous example.com> writes:
On 14.05.21 12:00, PinDPlugga wrote:
 Hi thank you both for your answers. I had understood from an earlier 
 chapter how this could introduce a bug, but I was confused because the 
 warning suggests attaching ```return``` to the parameter, which is empty 
 in the declaration.
`this` is considered a hidden parameter. Every non-static method has it.
 So my next question would be how come ref based operator overloads are 
 not labelled as return and do not show this warning?
 
 ``` D
 struct Fraction {
      auto n = 0L;
      auto d = 1L;
 
      ref Fraction opUnary(string op)()
      if (op == "++") {
          n += d;
          return this;
      }
 }
 
 ref Fraction foo() {
      auto f = Fraction(1, 3);
 
      // Same bug as with reduce
      return ++f;
 }
 ```
Note that opUnary is not an ordinary method. It's a template. That means attributes are inferred [1], including the `return` attribute. I.e., the compiler adds `return` to the signature for you, just like Steven suggested you do manually. Try omitting the return type of `reduce` in your original code: ref reduce() { ... } That also enables attribute inference, and your code will compile. [1] https://dlang.org/spec/function.html#function-attribute-inference
May 14
prev sibling parent Paul Backus <snarwin gmail.com> writes:
On Friday, 14 May 2021 at 10:00:28 UTC, PinDPlugga wrote:
 Hi thank you both for your answers. I had understood from an 
 earlier chapter how this could introduce a bug, but I was 
 confused because the warning suggests attaching ```return``` to 
 the parameter, which is empty in the declaration.
The error message here is definitely not as good as it could be. As a general rule, if you want to apply an attribute to the `this` reference in a member function, you write it after the parameter list: struct S { // ... auto memberFunc() const scope { // `this` is const and scope } auto anotherOne() share inout { // `this` is shared and inout } }
May 14