Function attribute best practices

Ali Çehreli acehreli at
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 {

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

int count;

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

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 

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

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

Help! :)


More information about the Digitalmars-d-learn mailing list