Thread overview
ref struct member function
May 14
ag0aep6g
May 13

Hi I am working through Programming in D. In one exercise I have a helper function in a struct

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
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
On 5/13/21 12:40 PM, Steven Schveighoffer wrote:
> 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

I was writing this example that shows a use case for the problem (marked with BUG below):

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

May 14

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):

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?

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
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

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
    }
}