暗黙的

多くの場合、 RFCまたは内部フォーラムでRustの設計について他のユーザーと話し合うと、明示性に関する特徴的な記述に気付きます。 通常、精神の何か:







< X>



はあまり明示的ではないので好きではありません。 すべての魔法は< Y>



で適切であり、Rustは明示的な言語なので、 < Z>



使用する必要があります。

このようなコメントは、有用なフィードバックをほとんど提供しないため、ひどく悩まされます。 彼らは、「明示的なものは暗黙的なものよりも優れている」(これは議論の余地のない文であると想定されている)、およびいくつかのデザインは代替物よりも明確でないと主張するだけです彼らのアプローチが望ましいということになる。







今年初めに発行されたメモの中で、アーロンは、コンテキストサイズ(推論のフットプリント)について議論することにより、説明可能性の問題の底辺に到達しようとしました。 彼は、特定の機会の明示的な設計を判断するための根拠を準備するために、「明示」と「暗黙性」の概念を構成要素に分解しようとしました。 私は問題について少し違った見方をし、「明示的」という言葉が意味することを一般的な用語で概説したいと思います。







英語はかなり曖昧な言語で、形容詞にはさまざまな文脈依存の意味があります。たとえば、前の文で「ファジー」という単語がどのように使用されているかなどです。 「明示的」という言葉もあいまいなので、誰かがこの言葉を誤用しているとは断言できません。 しかし、私は「顕在化」の議論で自分の考えをより明確に表現することを提案します。そうすれば、誰もが正確に議論されていることをよりよく理解することができます。







「錆は明示的な言語」とは



多くの場合、「明示的は暗黙的よりも優れている」という言葉に戸惑い、マニフェストは悪く、暗黙的であり、反対に良いと主張して、この問題の反対側を取りたいだけです。 Rustはかなり明示的だと思いますが、「明示的」という言葉を使用する場合、その言葉が通常意味するものよりも具体的なものを意味します。 私の意見: Rustは明示的です。なぜなら、ソースコードを読むだけでプログラムについて多くのことを理解できるからです







たとえば、Rustの構造定義は次のとおりです。







 struct Doggo { coat_color: Color, stamina: u32, love: u32, // NOTE:  true is_a_good_dog: bool, } struct Color(u8, u8, u8); struct TennisBall; struct Park { dogs: Vec<Doggo>, } struct Fetch<'a> { park: &'a Park, doggo: &'a Doggo, ball: TennisBall, }
      
      





これらの構造がメモリ内でどのように配置されるかについて、定義を見るだけで非常に多くのことが言えます。









暗黙性の例(上記のコンテキスト)は、これらの構造体のフィールドの正確な順序です。 Rustは構造内のフィールドの順序を明確に決定しないため、状況に応じて、何らかの方法でフィールドを再配置することで最適化できます。 通常、 unsafe



コードで作業する場合を除いて、この順序を知る必要はありません。







あなたのコードの多くの側面の同様の明示は通常非常に有用であり、Rustの強みだと思います。 しかし、それを維持するためには妥協する必要があることを覚えておく必要があります。たとえば、コンパイラは最適化中にスタックからヒープにデータを勝手に転送することはできません。







しかし、これは症状の非常に狭い定義です。 それは、ソースコードを手元に置いて、このプログラムに関するいくつかの質問に答えることができることを意味します。 ここで、「マニフェスト」の概念をいくつかのより具体的な概念に分割し、それらが言語の機能をどのように説明するかを検討します。







「明示的」という言葉の他の意味



明示的-うるさいという意味ではありません(冗長)



より軽量な構文の導入に関する議論では、明示的でないと宣言するユーザーをよく見ます。 コードに必要な情報が含まれている限り、コードは上記の意味で「明示的」です。 したがって、このプロパティを「ノイズ」と呼びます。







1つの例は、オペレーター言語の紹介?



これは前回のtry!



よりもわずかに短いtry!



。 一部のユーザーは、この演算子により、関数の早期終了を見逃しやすくなるという懸念を表明しています。 この場合、彼らは構文をより明示的ではなく、より騒々しいものにしたかったのです。







関数からのすべての戻り点は明示的である必要がありますが、必ずしもノイズが多いとは限りません。 つまり、関数がどのように値を返すかを知りたい場合、それができるはずですが、これはコードを読むときに注意を払う最初のことではありません。 それどころか、特に演算子を介してスタックにエラーをスローする場合は?



、コードを読むときに、早期終了は一般的に私にはほとんど興味がありません。







明示的-「面倒」を意味しない



ユーザーは、リソースを集中的に使用する操作の構文は、もう一度使用しないようにするには重くなければならないと言うことがあります。 たとえば、ユーザーは、スタック上でオブジェクトを作成するよりもヒープ上でオブジェクトを作成する方が、エレガントではない言語の利点を考慮するかもしれません。







そのような論争ではしばしば「顕在化」という言葉が使われますが、そのような「統語論的な塩」はそれに完全に直交しています。 実際、構造の使用が望ましくないことを示すために構造のより大きな「重さ」について話している。 たとえば属性[repr(boxed)]



想像できます。これは、タイプインスタンスが常にヒープに割り当てられることを意味します。 これは、共通のテンプレートを作成する非常に便利な形式かもしれません。







 struct Catters { inner: Box<CattersInner>, } struct CattersInner { color: Color, pounces: u32, naps: u32, meows: u32, } //  repr(boxed)    : #[repr(boxed)] struct Catters { color: Color, pounces: u32, naps: u32, meows: u32, }
      
      





このような属性は、コードの明示性を低下させることはありませんCatters



定義を見ると、メモリに保存されている方法についてまったく同じ情報をすぐに見ることができます。 ただし、このような属性を使用すると、タイプデータをヒープに簡単に配置できます。







以前のように、私は個人的にヒープに変数を配置するコードを書くのは面倒だとは思わない。 ヒープにデータを配置することがデフォルトの動作であるべきとは思えませんが、スタックに配置するよりもヒープに配置する方が望ましい状況はそれほど多くありません。 したがって、ユーザーに迷惑をかけないでください。ユーザーが何か間違ったことをしていると考える理由を与えてはなりません。







明示的-手動ではありません



また、「明示的」という言葉は、何かが起こるためにコードを書く必要があることを示すために時々使用されます。 何かが明確に定義された方法で発生し、それに関する情報がソースコードから簡単に取得できる場合、それは先ほど引用した狭い意味で「明示的に」再び使用されます。 代わりに、ユーザーが目的の動作を明示的に要求する必要があるため、一部のアクションは手動であると言わなければなりません。







たとえば、 drop



手動で呼び出す必要があるRustのバージョンを想像してください(ただし、現在のRustでは、このメソッドを呼び出すことはできませんが、たとえば、値によってselfを取ると仮定します)。 実際、Rustはデストラクタの呼び出しを厳密に保証しないため、さらに安全です。







 fn string_processing(string: String, numbers: &mut Vec<u32>) { substrings = string.split_whitespace().filter(|s| s.starts_with('$')); for substring in substring { let n = substring.parse().unwrap(); numbers.push(n); } //   ,    "" string.drop(); }
      
      





drop



コール回線を削除すると、回線に割り当てられたメモリがリークします。 そのようなアプローチがより悪いことは誰にとっても明らかだと思います。 デストラクタを自動的に呼び出すことには何も問題はありません。変数のスコープの終わりを追跡するだけで、いつデストラクタが発生するかを常に理解できるからです。







明示的-ローカルを意味しない



場合によっては、「明示性」という言葉で、ユーザーはコードの特定のセクション内の明示性を意味します。 これは、コードの特定の部分のみを学習することで、コードに関するいくつかの情報を明確にする必要があることを意味します。 さらに、モジュール、関数、式など、任意のサイズにすることができます。ソースコードの特定のセクションに明示的に何かがある場合、それが明らかにどこにでもあることを意味するわけではありません。







Rustの暗黙的な機能は、同時にローカルではありませんが、メソッドの解決です。 コードを見てください:







 fn main() { let mut vec = vec![0, 1, 2]; let x = vec.len(); vec.extend([x, x + 1]); for elem in vec.into_iter() { println!("{}", elem) } }
      
      





この関数では、 len



extend



およびinto_iter



3つの異なるベクトルメソッドを呼び出します。 それぞれが独自の方法で(参照、可変リンク、および値によって) self



を取ります。 Vec



構造自体に対して2つのメソッドが定義されており、1つはExtend



からのExtend



です。 指定された関数のみを見ると、この情報は表示されませんが、 Vec<T>



impl



ブロックを考慮すると、これらはすべて「明示的」になります。







それどころか、演算子?



そのような地域を所有しています。 Result



も返す関数から呼び出されるResult



を返すすべての関数に対して、演算子が自動的に適用されることを想像できます?



(これは、同様のJava言語での例外の動作方法です)。 ただし、暗黙的な早期終了が機能するかどうかを理解するために、関数インターフェイスを調べる必要はないと判断しました。 これは有用な地域の良い例だと思います。







おわりに



一般に、議論中に「明示的」という言葉を使用する場合、意味をより正確に言う必要があるかどうかを考えます。









これらの各用語-明示的、うるさい、重い、手動、ローカル-は、ある場合には使用に適しており、他の場合には不適切です。 ほとんどの場合、機能の実装へのアプローチを選択する場合、妥協が必要です。 選択を行う1つの方法は、それがコンテキストサイズにどのように影響するかを検討することです(Aaronによる説明)。







ですから、次回何らかの言語機能の顕在性について議論するときに、どのような顕在化を心配しているかをより正確に示し、提案がより合理的であると考える理由を明確に説明してください。







この記事の翻訳、校正、編集に貢献してくれた錆びたコミュニティの皆さんに感謝します。 つまり、 bmusinmkpankovvitvakatuおよびsasha_gav








All Articles