deleteがなくなる話

# はじめに

以前から「そのうち消すから使わないでね」と言われていたが、いつまでたっても消えないので、「このまま残ったままになるのでは・・・」と思われていた`delete`演算子ですが、dmd2.079.0（2018年3月1日リリース）で__ついに__廃止予定となりました。一定期間の後言語から削除される予定です。

（ちなみにdmd2.080.0で、カスタムアロケータ(クラスに独自の`new`/`delete`を定義するやつ)もひっそりと廃止予定となりました。）

変更からだいぶ時間が経っているのですが、~~ネタがない~~気付いたのが最近だったのと一応年内の出来事なので、今さらですが書いてみることにしました。


# 代替の機能

以下は`delete`を使用した際に表示される警告メッセージです。

```console
Deprecation: The `delete` keyword has been deprecated.
Use `object.destroy()`
 (and `core.memory.GC.free()` if applicable) instead.
```

代替機能として`object.destroy()`が案内されています。

__故__`delete`の動作は大まかには次の通りです。

1. オブジェクトの破棄（デストラクタ）
2. メモリの解放
3. 参照変数の初期化（nullの代入）

このうち1.を行うのが`object.destroy()`であり、2.を行うのが`core.memory.GC.free()`に相当します。
元の挙動が__どうしても__必要な場合、`core.memory.__delete()`を使用することもできます。

D言語的にはメモリの解放はGCに任せてほしい、という感じでしょうか。

---

`object.destroy()`の動作（クラスの場合）は

1. 初回のみ以下の処理を実行する
2. メンバ変数、および本体のデストラクタを再帰的に呼び出す
3. __メンバ変数を初期状態に戻す__

という感じで、`object.destroy()`後にオブジェクトに触っても（ある程度）安全なようになっています。

```d
int dtorCount;
class Hoge {
    string s = "XXXX";
    this() { s = "Hoge"; }
    ~this() { ++dtorCount; }
}
void main() {
    auto p = new Hoge();
    assert(dtorCount == 0);
    assert(p.s == "Hoge");
    .destroy(p);            // 1回目は実行される
    assert(dtorCount == 1);
    assert(p.s == "XXXX");  // 初期状態に戻っている
    .destroy(p);            // 2回目以降は何もしない
    assert(dtorCount == 1);
    assert(p.s == "XXXX");
}
```

注意点として、`object.destroy()`はGCにはノータッチ（GCを実行したりメモリを解放したりしない）とドキュメントには書いてあるのですが、現時点では`@nogc`ではないようです（`object.destroy()`の問題ではなく、クラスのデストラクタの問題）。

```d
class Hoge {
    void print(string s) @nogc {
        import core.stdc.stdio;
        fprintf(stdout, "%s", s.ptr);
    }
}
void main() @nogc {
    import core.stdc.stdlib, std.conv;
    // GCを使わずに、malloc, freeでメモリ確保
    enum size = __traits(classInstanceSize, Hoge);
    void* mem = malloc(size);
    scope(exit) free(mem);
    // emplaceでインスタンスを割り当て
    auto hoge = emplace!Hoge(mem[0 .. size]);
    // 普通に使用できる
    hoge.print("hello");

    .destroy(hoge);  // Error: `@nogc` function `D main` cannot call non-@nogc function `object.destroy!(true, Hoge).destroy`
}
```

```d
class Hoge { }
void main() @nogc {
    import std.experimental.allocator;
    import std.experimental.allocator.mallocator;
    auto p = Mallocator.instance.make!(Hoge)();
    // 同じ理由でこれもダメ
    Mallocator.instance.dispose(p);  // Error: `@nogc` function `D main` cannot call non-@nogc function `std.experimental.allocator.dispose!(shared(Mallocator), Hoge).dispose`
}
```

すぐには[修正が難しい問題](https://forum.dlang.org/thread/pnswjnosrlfazgscuhrs@forum.dlang.org)らしく、ちょっと残念。


# 廃止の理由

公式サイトの[Deprecated Features](https://dlang.org/deprecate.html)（廃止予定の言語機能についてまとめたページ）の`delete`の項には

> delete makes assumptions about the type of garbage collector available that limits which implementations can be used, and can be replaced by a library solution.

`delete`が存在することで、D言語で使用可能なGCの種類が制限され、またライブラリ実装で置き換えが可能なため、とあります。
（※ちなみに[InternetArchive](https://web.archive.org/web/20120702222756/https://dlang.org/deprecate.html)によると、2012年7月2日には既にこの記載がありました・・・。）

これだけだと、`delete`は標準のGC実装に依存しているので、引き剥がすことが目的という風にも読めます。


# ここがダメだよ`delete`

`delete`の問題点として思いついたものを書いてみます。

* オブジェクトへの参照が複数ある場合、`delete`後も解放済みのメモリにアクセス可能になる

```d
import std.stdio;
class Hoge {
    string s_ = "XXXX";
    this(string s) { s_ = s; }
    ~this() { "%s.dtor()".writefln(s_); }
}
void main() {
    /// gc
    {
        scope a1 = new Hoge("a");  // オブジェクトを参照するa1とa2が消えてから
        scope a2 = a1;             // GCによってファイナライズされるので安全
    }
    "---".writeln();

    /// delete
    {
        scope b1 = new Hoge("b");
        scope b2 = b1;
        delete b1;                 // オブジェクトは削除され、b1はnullになるが
        b2.__dtor();               // b2経由で解放済みのメモリにアクセス可能であり危険
    }
}
```

```console:
a.dtor()
---
b.dtor()
XXXX.dtor()
```

* `const`/`immutable`な変数の場合、`delete`では最後の`null`の代入ができずにエラーになる（それはそう）

```d
class Hoge { }
void main() {
    import core.memory;
    const p = new Hoge();
    delete p;  // Error: cannot modify `const` expression `p`
    .destroy(p);  // Ok
    GC.free(cast(void*)p);  // Ok
}
```


# おわりに

今年はあまりD言語の情報を追えていなかったのですが、地味に色々と進化していて面白かったです。