LoginSignup
10
4

More than 5 years have passed since last update.

RustでCall Operator Overloadしようとして躓いた。

Posted at

背景

プログラミングする際にCall OperatorをしょっちゅうOverloadして使うんですがC++とかPythonと違ってOverloadの仕方がちょっと特殊?な上に全然関連ドキュメントが出てこないので備忘録がてらにメモしておきます。

C++/PythonでのOperator Overload

自分がある程度書いていてOperator Overloadができる言語がC++/Pythonあたりしかなかったのでその2言語での例になりますがやり方はいずれも以下の通りです。

C++

#include <iostream>
#include <string>

class Foo {
public:
  void operator()(std::string arg) {std::cout << "Hello, " << arg << "!" << std::endl;}
};

Python

class Foo:
  def __init__(self):
    pass

  def __call__(self, arg):
    print "Hello {}!".format(arg)

いずれもクラス定義の中で関連する関数をOverloadすればおしまいですね。

RustでのOperator Overload

普通の演算子のOverloadでさえちょっと面倒なのにCall Operatorでは更に一手間必要です。というのもRustにはオブジェクトという概念が存在しないのでCall OperatorをもったTraitを実装してやることでOperator Overloadを実現するんですがそのためのTraitが三種類 (FnOnce, FnMut, Fn) 存在します。そしてFnMutの実装にはFnOnceの実装が必要で、Fnの実装にはFnMutの実装が必要というなんとも面倒な仕様になっています。

use std::ops::{FnOnce, FnMut, Fn};

struct Foo {}

impl FnOnce for Foo {
  type Output = (); // 返り値は無し
  extern "rust-call" fn call_once(self, (arg,): (String,)) -> Self::Output {
    println!("Hello {}!", arg);
  }
}

impl FnMut for Foo {
  extern "rust-call" fn call_mut(&mut self, (arg,): (String,)) -> Self::Output {
    println!("Hello {}!", arg);
  }
}

impl Fn for Foo {
  extern "rust-call" fn call(&self, (arg,): (String,)) -> Self::Output {
    println!("Hello {}!", arg);
  }
}

selfが呼び出し時にconsumeされてもいいのであればFnOnceだけ実装してあげればいいんですがそうでなく単純にImmutableなReferenceを貸したいだけの時はFnMutFnを実装してやる必要があります。なんでこうなったのかはわかりませんがそういうものみたいです。めんどくさい。

10
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
10
4