返回类型属性:auto, ref, inout, 和 auto ref
auto:不需要指定函数返回类型,即可变,占位符.有多个语句,返回各种返回类型公共类型,如双精的公共类型为双精,则返回双精.

auto func(int i) {
    if (i < 0) {
        return i;      // 整
    }
    return i * 1.5;    // 双精
}

void main() {
    auto result = func(42);
    static assert(is (typeof(result) == double));
}

引用,表示按引用参/返回值,没有它,默认按传递.

int greater(int first, int second) {
    return (first > second) ? first : second;
}
import std.stdio;

void main() {
    int a = 1;
    int b = 2;
    int result = greater(a, b);
    result += 10;//a,b都不变
    writefln("a: %s, b: %s, result: %s", a, b, result);
}
//改一下;
ref int greater(ref int first, ref int second) {
    return (first > second) ? first : second;
}
 int a = 1;
    int b = 2;
    greater(a, b) += 10;         //a,b都变了
    writefln("a: %s, b: %s", a, b);
//注意
    int result = greater(a, b);//返回的引用复制了一份给本地变量.
    result += 10; //这里不会影响a,b. 因为result是值类型,复制了一份. 
//指针 
    int * result = &greater(a, b);
    *result += 10;
    writefln("a: %s, b: %s, result: %s", a, b, *result)
//指针当然要变了.

不能返回本地变量的引用.

ref string parenthesized(string phrase) {
    string result = '(' ~ phrase ~ ')';
    return result;    // 
} // 生命期在此结束

返回的引用应该是在函数之前就存在的变量的引用.
编译器会检查的
auto ref表示模板.

auto ref string parenthesized(string phrase) {
    string result = '(' ~ phrase ~ ')';
    return result;//能编译.
}

意思就是生命期比函数大的返回引用.小的返回副本.
尽量返回引用的意思.
在模板参数要看上下文才知道是引用还是副本时有用.
inoutconst,immutable,和mutable的通配符.

string parenthesized(string phrase) {
    return '(' ~ phrase ~ ')';
}
// ...
    writeln(parenthesized("hello"));
inout(char)[] parenthesized(inout(char)[] phrase) {
    return '(' ~ phrase ~ ')';
}

扩展一下,更通用了.类似模板,但比模板更限制.只限制可变属性.先推导出具体的可变属性.就像占位符一样.

    char[] m;
    writeln(typeof(parenthesized(m)).stringof);

    const(char)[] c;
    writeln(typeof(parenthesized(c)).stringof);

    immutable(char)[] i;
    writeln(typeof(parenthesized(i)).stringof);
//输出
char [] 
const(char)[] 
string

行为属性.pure, nothrow, 和 @nogc
无副作用:正确性,维护性.
D的:不访问可变全局和静态变量的就是纯.由于输入输出流是可变全局.所以纯函数中不能输入/出.
如果函数仅访问参数,本地变量,全局不变状态/变量,则是纯.
另外,纯函数中也允许改变全局状态的以下操作:
1,用new分配内存
2,终止程序
3,访问浮点处理标志
4,抛异常.
纯函数不能调用不纯函数.
pure关键字指定该函数应该如上表现,由编译器保证.

import std.stdio;
import std.exception;

int mutableGlobal;
const int constGlobal;
immutable int immutableGlobal;

void impureFunction() {
}

int pureFunction(ref int i, int[] slice) pure {
    enforce(slice.length >= 1);//可抛

    i = 42;slice[0] = 43;//可改参数

    // 可访问不变全局状态
    i = constGlobal;i = immutableGlobal;
    auto p = new int;// 可用`new`式
    // 不能访问全局可变状态
    i = mutableGlobal;    // 编译错误

    // 不能处理输入输出操作
    writeln(i);           // 编译错误

    static int mutableStatic;

    // 不能访问可变静态状态
    i = mutableStatic;    // 编译错误

    // 不能调用非纯函数:
    impureFunction();     // 编译错误

    return 0;
}

void main() {
    int i;
    int[] slice = [ 1 ];
    pureFunction(i, slice);
}

这样,对于给定的值,返回的值都是一样的.不变.方便优化.
模板的纯度取决于参数,需要推导,当然也可以指定,类似的,函数的纯度也是推导的.

import std.stdio;

// This template is impure when N is zero
void templ(size_t N)() {
    static if (N == 0) {
        writeln("zero");//纯,则不能有输入出
    }
}

void foo() pure {
    templ!0();    // 编译错误
}

void main() {
    foo();
}
//
void foo() pure {
    templ!1();//编译
}

上面可以为纯.

import std.stdio;

debug size_t fooCounter;

void foo(int i) pure {
    debug ++fooCounter;

    if (i == 0) {
        debug writeln("i is zero");
        i = 42;
    }

    // ...
}

void main() {
    foreach (i; 0..100) {
        if ((i % 10) == 0) {
            foo(i);
        }
    }

    debug writefln("foo is called %s times", fooCounter);
}

输入出不为纯,太严格,放松一下,用debug
debug修改全局,打印信息,不纯.但标记为debug.当有-debug开关时才加相应调试句子.

interface Iface {
    void foo() pure;    // 子类必须纯

    void bar();         // 可为纯
}

class Class : Iface {
    void foo() pure {   // 必须
        // ...
    }

    void bar() pure {   // 不必须
        // ...
    }
}

成员函数可标记为纯,子类函数可覆盖父类不纯函数,但反过来不行.
闭包和λ函数也可为纯,类似模板,编译器推导函数/闭包/auto函数是否为.

import std.stdio;

void foo(int delegate(double) pure dg) {
//要求参数为纯,
    int i = dg(1.5);
}

void main() {
    foo(a => 42);                // 编译

    foo((a) {                    // 编译错误
            writeln("hello");//不纯
            return 42;
        });
}

最好,函数记录在特定错误条件下可能抛的异常的类型,一般调用者假定都可抛(现在好像要改了,以不抛作为默认类型)
不抛保证函数不会抛出异常.
不建议抓错误或其基类可抛.这表示任何异常(不能区分/细分异常.不抛函数仍可发出致命错误的子类,表示不可恢复.不抛函数自身不能抛,其也不能调用函数.就是可恢复.不然就是错误`.

int add(int lhs, int rhs) nothrow {
    writeln("adding");    // 编译错误
    return lhs + rhs;
}

writeln不是且不能是一个不抛函数.

int add(int lhs, int rhs) nothrow {
    int result;

    try {
        writeln("adding");    // 编译
        result = lhs + rhs;

    } catch (Exception error) {   // 抓所有异常
        // ...
    }//都抓了异常了.

    return result;
}

把所有异常都抓了,所以就不可能再异常了.
nothrow不包含Error的子类异常,

int foo(int[] arr, size_t i) nothrow {
    return 10 * arr[i];
}

尽管可能会抛区间错误,但仍可标记为不抛.因为已经进入错误的范围了,这不可恢复,抛/不抛都无所谓了.
与纯一样,编译器自动推导闭包,模板,λ函数不抛.
@nogc
d是垃集语言,许多结构与算法都用垃集.通过垃集算法回收动态内存块.
常用D操作利用垃集,比如切片的增加元素:

int[] append(int[] slice) {
    slice ~= 42;
    return slice;
}

垃集是昂贵操作,使程序变得相当慢,内存使用变大.
@nogc表明程序不用垃集.

void foo() @nogc {
    // ...
}

编译器保证@nogc不包含垃集操作.

void foo() @nogc {
    int[] slice;
    // ...
    append(slice);    // 编译错误,
//不能调用非`@nogc`函数
}

代码安全属性:@安全,@信任,@系统.
编译器会推导模板,闭包,λ函数,自动函数的安全级.
@安全:
一类编程错误是无意的在不相关的内存位置上写.
这是由于错误使用指针类型转换造成的.
@safe保证,不包含损坏内存操作.在@safe函数中,编译器不允许:
1,不能将指针转为除void星外其他指针类型.
2,非指针表达式不能转为指针.
3,不能修改指针.
4,不能使用有指针和引用成员的联.
5,不能调用@系统函数.
6,不能抓异常的后代.
7,不能用内联汇编
8,可变不变不能相互转换.
9,线程本地变量共享不能相互转换.
10,不能取函数局部变量的地址.
11,不能访问_ _gshared变量.

@信任:
有些函数是安全的,但不能标记为@安全(太严格了),如必须调用不支持安全的C库.或虽然有不允许的操作,但经过严格测试证明是正确的.
@trusted程序员保证的内存安全.编译器不再检查.允许@安全代码调用@信任.
@系统,默认的安全属性,以后可能会改为@安全.
ctfe

import std.stdio;
import std.string;
import std.range;

string menuLines(string[] choices) {
    string result;

    foreach (i, choice; choices) {
        result ~= format(" %s. %s\n", i + 1, choice);
    }

    return result;
}

string menu(string title,string[] choices,size_t width) {
    return format("%s\n%s\n%s",title.center(width),'='.repeat(width),    // horizontal line
                  menuLines(choices));
}

void main() {
    enum drinks =menu("Drinks",[ "Coffee", "Tea", "Hot chocolate" ], 20);

    writeln(drinks);
}

ctfe,编译时执行函数,必须:
初化静/枚变量,计算固定数组长度,计算模板值参数.
不是所有函数都能编译时执行.如访问全局变量的函数就不行.因为运行时才有全局变量.同样stdout也是运行时函数.
_ _ctfe
需要结果时,编译时与运行时都可用,
_ _ctfe用于区分仅用于编译时运行时的函数.

import std.stdio;

size_t counter;

int foo() {
    if (!__ctfe) {
        //仅
        ++counter;
    }

    return 42;
}

void main() {
    enum i = foo();
    auto j = foo();
    writefln("foo is called %s times.", counter);
}