eval
ruby の機能を何度も使用しています。eval
しかし、 sは厄介だと人々が言っているのを聞いたことがあります。その理由と方法を尋ねられたとき、私はそれを使用しないという説得力のある理由を得ることができませんでした. 彼らは本当に厄介ですか?はいの場合、どのような方法で?評価するための可能な「より安全な」オプションは何ですか?
7 に答える
eval
安全でないだけでなく(他の場所で指摘されているように)、遅いです。実行されるたびに、eval
ed コードの AST を新たに解析する必要があります (JRuby などでは、バイトコードに変換する必要があります)。プログラムはそれほどeval
多くないため、インタープリターの対応する部分は大きいだけでなく、キャッシュコールドです)。
なぜeval
Rubyにあるのですか?ほとんどの場合「できるから」 - 実際、eval
(LISP プログラミング言語用に) が発明されたとき、それは主に見せるためのものでした。さらに言えば、eval
「インタープリターにインタープリターを追加」したい場合、プリプロセッサー、デバッガー、またはテンプレート エンジンの作成などのメタプログラミング タスクに使用するのは正しいことです。このようなアプリケーションの一般的なアイデアは、いくつかの Ruby コードをマッサージして呼び出すeval
ことです。これは、ドメイン固有のおもちゃの言語を再発明して実装することよりも確実に優れています。注意点は次のとおりです。コストに注意してください。たとえば、テンプレート エンジンの場合は、すべてのeval
ing を実行時ではなく起動時に行います。そしてしないでくださいeval
それを「飼いならす」方法を知らない限り、信頼できないコード、つまり、機能規律の理論に従って言語の安全なサブセットを選択して強制します。後者は非常に難しい作業です (たとえば、Java でどのように行われたかを参照してください。残念ながら、Ruby でそのような作業が行われたことは知りません)。
Ruby には、以下よりも適切なギミックがいくつかありますeval()
。
#send
名前が文字列であるメソッドを呼び出して、それにパラメーターを渡すことができるものがあります。yield
受信側メソッドのコンテキストで実行されるメソッドにコード ブロックを渡すことができます。- 多くの場合、名前が文字列であるクラスを取得するには単純
Kernel.const_get("String")
で十分です。
詳しく説明できないと思いますので、ヒントだけお伝えしましたので、興味のある方はググってみてください。
デバッグが困難になります。最適化が難しくなります。しかし、何よりも、それは通常、あなたがやろうとしていることを行うためのより良い方法があるという兆候です.
で何を達成しようとしているのかを教えていただければeval
、特定のシナリオに関連するより適切な回答が得られる場合があります。
Eval は非常に強力な機能であり、慎重に使用する必要があります。Matt J によって指摘されたセキュリティの問題に加えて、実行時に評価されたコードのデバッグが非常に難しいこともわかります。実行時に評価されるコード ブロックの問題は、インタープリターが表現するのが難しいため、それを探すのが難しくなります。
そうは言っても、その問題に慣れていて、セキュリティの問題を心配していないのであれば、Ruby を魅力的なものにしている機能の 1 つを使用することを避けるべきではありません。
特定の状況では、適切な配置eval
は賢く、必要なコードの量を減らします。Matt J が言及したセキュリティ上の懸念に加えて、非常に簡単な質問を 1 つ自問する必要があります。
すべてが完了したら、他の誰かがあなたのコードを読んで、あなたが何をしたかを理解できますか?
答えが「いいえ」の場合、 で得たものeval
は保守性のために見捨てられます。この問題は、チームで作業している場合だけでなく、自分自身にも当てはまります。今から数年ではなくても、自分のコード月を振り返って、自分が何をしたかを知りたいのです。
「外部」から取得したものを に渡す場合は、eval
何か間違ったことをしていて、非常に厄介です。コードを安全にエスケープするのは非常に難しいため、非常に安全ではないと考えています。ただし、次のコード例のように重複を避けるために eval を使用している場合は、使用しても問題ありません。
class Foo
def self.define_getters(*symbols)
symbols.each do |symbol|
eval "def #{symbol}; @#{symbol}; end"
end
end
define_getters :foo, :bar, :baz
end
ただし、少なくとも Ruby 1.9.1 では、Ruby には非常に強力なメタプログラミング手法があり、代わりに次のことを行うことができます。
class Foo
def self.define_getters(*symbols)
symbols.each do |symbol|
define_method(symbol) { instance_variable_get(symbol) }
end
end
define_getters :foo, :bar, :baz
end
ほとんどの場合、これらのメソッドを使用する必要があり、エスケープは必要ありません。
もう 1 つの悪い点eval
は、(少なくとも Ruby では) インタープリターが文字列を解析してから現在のバインディング内のコードを実行する必要があるため、かなり遅いという事実です。他のメソッドは C 関数を直接呼び出すため、速度が大幅に向上するはずです。