GoでRAIIを満たしたい！

#RAIIとは
Resource Acquisition Is Initialization
リソースの確保は初期化時に
という意味であるが、コンストラクタ、デストラクタのある言語では具体的に
コンストラクタでリソースを確保し、デストラクタで開放
という事になる

##RAIIしていない例

エラーチェックとか無視してるけど

```cpp
#include <string>
#include <stdio.h>

class Hoge{
private:
  FILE *fp;
public:
  void Open(const char *file){
    fp = fopen(file, "r");
  }
  
  void Close(){
    fclose(fp);
  }
};

int main()
{
  auto hoge = new Hoge();
  // x ここはリソースが確保されていないのでReadWriteするとエラー
  hoge->Open("xxx.txt");
  // o ここはリソースが確保されている
  hoge->Close(); // x Close忘れるとリソースがリークする
  // x Close後にアクセスするとエラー
  delete hoge;
}
```

色々とバグが潜む隙があるので、RAIIを行うには下記のようにします
（本当は unique_ptrなどを使うが、今回はC++については　細かく扱わない）

```cpp
#include <string>
#include <stdio.h>

class Hoge{
private:
  FILE *fp;
  Hoge();
public:
  Hoge(const char *file){
    fp = fopen(file, "r");
  }
  
  virtual ~Hoge(){
    fclose(fp);
  }
};

int main()
{
//  auto hhh = new Hoge();  // o デフォルトコンストラクタは禁止
  auto hoge = new Hoge("xxx.txt");
  // o ここはリソースが確保されている
  delete hoge;  // o デストラクタで確実に開放される
}
```

これで、クラスの生存期間とリソースの確保期間が一致し、不具合の潜む箇所が減りました


#Goでは？
Goにはコンストラクタもデストラクタもありません

##New関数
Goでは NewHoge という関数をコンストラクタとする習わしがあります

```go

type MyFile struct {
    fp       *os.File
    filename string
}

func NewMyFile(filename string) *MyFile{
    fp, _ := os.Open(filename)
    return &MyFile{filename: filename, fp: fp}
}

func (s *MyFile) Read(len int)  {
    // Read処理
}

func (s *MyFile) Close()  {
    s.fp.Close()
}

func main() {
    {
        mf = NewMyFile("hoge.txt")    // o コンストラクタでリソース取得
        mf.Read(10) // o リソース確保されている
        mf.Close()  // x Close忘れるとリークする
        // x Closeしているのでここでアクセスしたらエラー
    }
}

```

何個かの問題が解決したように思えますが、実は全然解決していない
例えばメイン関数でNewを使わず直接構造体生成された場合

```go
func main() {
    {
        mf = &MyFile{}    // x 直接生成された
        // x リソースが確保されていない
        mf.Open("xxx.txt") // o リソース確保されている
        mf.Read(10) // o リソース確保されている
        mf.Close()  // x Close忘れるとリークする
        // x Closeしているのでここでアクセスしたらエラー
    }
}
```

と、Newを使わずに構造体を直接生成されるとアウトだ


##Private構造体にする
構造体をPrivate(頭文字を小文字)にすると、別のパッケージからは直接生成不可能になる


```go:MyFile/myFile.go
type myFile struct {
    fp       *os.File
    filename string
}

func NewMyFile(filename string) *myFile{
    fp, _ = os.Open(filename)
    return &myFile{filename: filename, fp: fp}
}

func (s *myFile) Read(len int)  {
    // Read処理
}

func (s *myFile) Close()  {
    s.fp.Close()
}
```

```go:main.go
func main() {
    {
        mf = MyFile.NewMyFile("hoge.txt")    // o コンストラクタでリソース取得
        mf.Read(10) // o リソース確保されている
        mf.Close()  // x Close忘れるとリークする
        // x Closeしているのでここでアクセスしたらエラー
    }
    {
        // mf = &MyFile.myFile{}  // o Private構造体は直接生成できない
    }
}

```

##コールバックを使い開放をする
Private構造体にする前のものに改造を行う

Newxxx関数に、リソース確保したら呼び出すコールバック関数を渡す
これでNewxxx内部でリソースの確保と開放が制御できる
ここでは deferを使う
deferは、その関数の終了時に実行される関数を指定できる

```go

type MyFile struct {
    fp       *os.File
    filename string
}

func NewMyFile(filename string, cb func(*MyFile) error) error {
	fp, err := os.Open(filename)
	s := &MyFile{filename: filename, fp: fp}
	if err != nil {
	    return err
	}
	defer s.fp.close()

	err = cb(s)

	return err
}

func (s *MyFile) Read(len int)  {
    // Read処理
}

func (s *MyFile) close()  {
    s.fp.Close()
}

func main() {
    _ = NewMyFile("hoge.txt", func(mf *MyFile) error {
        mf.Read(10)
        return nil
    })
}
```

##private構造体とコールバックを組み合わせる

main関数から *myFile が参照できずコンパイルエラー

```go
func main() {
    _ = NewMyFile("hoge.txt", func(mf *myFile) error {   // ここがエラー
        mf.Read(10)
        return nil
    })
}
```


##結論、Interfaceを使う
上記は *myFile がコールバック型で参照できないので、Interfaceにする

```go:MyFile/myFile.go

type ImyFile interface {
	Read(len int) 
}

type myFile struct {
	fp       *os.File
	filename string
}

func NewMyFile(filename string, cb func(ImyFile) error) error {
	fp, err := os.Open(filename)
	s := &myFile{filename: filename, fp: fp}
	if err != nil {
		return err
	}
	defer s.fp.Close()

	err = cb(s)

	return err
}

func (s *myFile) Read(len int) {
    // Read処理
}
```

```go:main.go
func main() {
    _ = MyFile.NewMyFile("hoge.txt", func(mf MyFile.ImyFile) error { // o 初期化時にリソース確保
        mf.Read(10)
        return err
        // o コールバック抜けた後、New関数を抜け、deferにてCloseが呼ばれるので開放忘れなし
    })

    // mf := &MyFile.ImyFile{}  // o Interfaceは生成不可能
    // mf := &MyFile.myFile{}  // o Privateなので構造体を直接生成不可能
}

```


冗長なコードになったけど、これでGoでも RAIIを守れたんじゃないかな？