Function attribute best practices

Ali Çehreli acehreli at yahoo.com
Mon Sep 12 16:14:42 UTC 2022


The following range Foo is trying to be helpful by adding as many 
attributes as it can ('const' is missing because ranges cannot be 
'const' because at least popFront() needs to be mutable):

import std.algorithm;

struct Foo(R) {
     R r;
     int i;

     bool empty() @nogc nothrow pure @safe scope {
         return r.empty;
     }

     auto front() @nogc nothrow pure @safe scope {
         return r.front;
     }

     auto popFront() @nogc nothrow pure @safe scope {
         r.popFront();
     }
}

auto foo(R)(R r) {
     return Foo!R(r);
}

int count;

void main() {
     [ 1, 2 ]
         .map!((i) {
                 ++count;    // <-- Impure
                 return i;
             })
         .foo;
}

Of course there are compilation errors inside the member functions 
because e.g. r.front that it dispatches to is not pure (it touches the 
module variable 'count'):

   Error: `pure` function `deneme.Foo!(MapResult!(__lambda1, 
int[])).Foo.front` cannot call impure function 
`deneme.main.MapResult!(__lambda1, int[]).MapResult.front`

(Other attributes would cause similar issues if e.g. the lambda were @nogc.)

What are best practices here?

Is this accurate: Because Foo is a template, it should not put any 
attribute on member functions? Or only member functions that use a 
member that depends on a template parameter? And non-members that are 
templates?

It is scary because Foo works just fine until it is used with impure code.

Is putting function attributes on unittest blocks for catching such issues?

@nogc nothrow pure @safe
unittest
{
     // ...
}

No, it isn't because unless my unittest code is impure, I can't catch my 
incorrect 'pure' etc. on my member functions.

Help! :)

Ali


More information about the Digitalmars-d-learn mailing list