rust-lang / rfcs#2000の追跡の問題
更新:
あなたが手助けしたい場合は、見とるオープンのconstジェネリック医薬品の問題をと始めるに助けのためのping @varkorに、@eddyb、@yodaldevoid、@ OLI-OBKまたは@lcnrをお気軽に!
ブロッキングの安定化:
残りの実装の問題:
FIXME(const_generics)
コメントを解決します。FIXME(const_generics:defaults)
)。has_infer_types
使用を監査します。{X * 2}
。ConstEvaluatable
述語を追加し、 expr
は遅延評価されるため(整数リテラルであっても)、 WF([T; expr])
はConstEvaluatable(expr)
必要とします。 この述語を満たすには、式が正常に評価される必要がありますが、正規化ではエラーが無視され、見つかったUnevaluated
式はそのままになります。これは、関連する型の射影で発生することです。 同じシステムがジェネリックスを構成するように拡張できることを期待しています。@EpicatSupercellはこれに取り組むことに関心を示しており、最初の実装を通じて彼らを指導します。 ただし、#44275で説明されている制限があるため、行き過ぎはできません。
つまり、 @ nikomatsakisの遅延正規化が必要です。これにより、型に埋め込まれた定数式が、循環依存関係を半分の時間生成することなく、スコープ内の境界を(関数/型定義/ implなどの項目から)監視できるようになります。
実装のウェイポイント(より直接的なメンタリングについては、Gitterで@eddyb
またはIRCでeddyb
を探してください):
const
パラメーターを追加しますparse_generic_params
- const IDENT: Type
解析しますDefId
コレクターty::Generics
-タイプパラメータの横にconst
パラメータを追加しますgenerics_of
- ty::ConstParameterDef
からhir::ConstParam
ty::ConstParameterDef
を作成しますDef
- const
パラメーターのバリアントを追加しますwith_type_parameter_rib
-型とconst
ジェネリックの両方をサポートConstVal
- ty::TyParam
類似したParam
バリアントを追加しますTyArray
Def::ConstParam
解決されたExprPath
の長さのTyArray
は、同様にConstVal::Unevaluated
ではなくConstVal::Param
を使用する必要がありますDef::TyParam
がty::TyParam
変わる方法のファッションsubst::Kind
- &ty::Const
をサポートし、 as_type
とas_region
がチェックされている場所でもas_const
をチェックしますConstVal
- ty::ReVar
/ ty::TyInfer(TyVar(_))
類似したty::ReVar
InferVar
バリアントを追加しますInferCtxt
- const
int_unification_table
とそのUnifyKey
implに対応するint_unification_table
ty::relate::{Relate,TypeRelation}
- ty::Const
に関連するサポート、およびsuper_combine_tys
が型に対して行うのと同様の方法でConstVal::InferVar
を処理しますこれはすべてimpl<T, const N: usize> Trait for [T; N] {...}
を許可する必要がありますが、実際には定数式を型/関数に渡さないことに注意してください(例: ArrayVec<T, 3>
。
私はこれを突き刺したいです:)
@jplatte @eddybこれに関するニュースはありますか?
@samsartorは、メインの実装作業の前に実行する必要のあるリファクタリングがありました。 これでほぼ完了です(現在、フィードバックを待っています)。 その後、実際にどれだけの仕事があるのかわかりません。 const paramsの解析は、リファクタリングの前に最初に始めたものでした。 まだ終わっていませんが、比較的早くできるはずです。
@jplatteプルリクエストをリンクできればいいですね。 私はそれを見つけることができませんでした。
PRはまだありません。 私のすべての仕事はここにあります。
これまでのところ素晴らしい仕事
基礎のPRがあります( Generics
リファクタリング):#45930
これはすでに含まれていると思いますが、さまざまな長さの線形代数スタイルのn次元配列、IE a 4x4 [[f64; 4]; 4]または4x3x5x6次元配列[[[[ f64; 6]; 5]; 3]; 4]であり、適切にラップして、適切なサイズのベクトルなどのスカラーの適切な特性実装の両方に特化したメソッドを生成できます。https://gist.github.com/huhligを参照して/ 8b21850b54a75254be4b093551f8c2cbを
以前、このRFCの一部として、Rustのフォールド式を提案した人は誰も覚えていません。 しかし、これはRustなので、新しい専用構文ではなく、通常のマクロとしてfold式を実装できなかった理由はありますか?
#45930がマージされた今、次のステップは何ですか? @jplatte
@ jplatte @ kjetilkjekaに言及するつもりだったと思います。
更新していただきありがとうございます! この機能を楽しみにしているのは私だけではないと確信しています。 良い仕事を続けてください!
@jplatteは焦りたくないのですが、これについて何か作業はありましたか? 何か手伝いましょうか? 誰かが手伝うべきですか?
@ est31ごめんなさい。 しばらくの間、これに取り組む時間がありませんでした、そして、私はいつ時間があるか完全にわかりません。 たぶん、他の誰かが私が中断したところから続けるのが最善です。 解析部分はほとんど完了していると思います。 ジェネリックパラメーターがconstパラメーターである場合にどうすればよいかわからなかったコード内の2つの場所があります。
また、constジェネリックスの解析(および同時に更新したきれいに印刷されたコード)のテストもまだありません。 他の誰かがこれに取り組み続けるために必要/役立つ他の情報を提供できるかどうかはわかりませんが、コードについて不明な点がある場合は、遠慮なくpingしてください。
@ jplatte〜最初はNone
、2番目は何も~~(cc @pnkfelix @nikomatsakis)
編集:どちらの場合も何もしません。
@eddyb最初のリンクされたスニペットについて、 None
が返されることを確認しますか? ここで何が起こっているのか理解できないかもしれませんが、何もすべきではないように思えます。 None
が返された場合、安全でない可能性のある他のパラメーターはスキップされます。
@yodaldevoid申し訳ありませんが、その通りです。これがGenerics
にあることに気づいていませんGenerics
。
@jplatteが行けるかどうかを確認するだけの時間があったので、
@yodaldevoid聞いて
@yodaldevoid :コラボレーションに参加しませんか? 私もこの問題について調査を行っています(😅)—おそらくお互いの仕事のギャップを埋めることができます。 (私がこれまでに行ったことをここで確認でき
@varkorあなたは私が得たよりもはるかに進んでいるようです。 私は確かに協力したいと思っています。 ある時点でIRCであなたをつかもうとしますが、その間に、あなたがまだ行っていないことを私が行ったかどうかを確認します。
これに進展はありますか?
組み込み用のコードを書いていますが、constジェネリックが本当に必要です。
@ qwerty19106
ゆっくりではありますが、これについては進展が見られます。 @varkorと私は、時間のあるときにこれに何度も取り組んできました。 今週は少し進歩しましたが、トンネルの終わりに基本的な使用法の光が見えています。
constジェネリックを実装するだけでなく、これをすべて可能にするために、私たち(varkor)はいくつかのクリーンアップを行いました(#48149および#48523を参照)。 現在の計画では、constジェネリックをプルする前に、これら2つのプルリクエストが通過するのを待つことだと思いますが、varkorはそれについてもっと話すことができます。
組み込み作業のためのconstジェネリックの必要性を本当に理解しています。 私はconstジェネリックが本当に欲しいので、これを始めました。そうすれば、クリーンアップして、タイプセーフな大量の埋め込みコードを作成できます。
TL; DR:進歩は進んでいますが、これは複雑です。 埋め込まれた正面にあなたを感じます。
「ドキュメント」チェックボックスについては、 rustcガイドに何かを記載して
返信ありがとうございます@yodaldevoid 。 私はあなたの仕事の終わりを楽しみにしています。
好奇心旺盛な人のために更新してください(私も好奇心が強いので)。 日時:2つのPRを挙げる上:#48523合併しており、#48149は着実に進展しています。
@ mark-imいいものだ! @varkorによる素晴らしい仕事。 ETAは大まかにいつですか、知っていますか? :-)
@alexreg https://github.com/rust-lang/rust/issues/51192#issuecomment -394126083
乾杯、@ flip111。
2番目の大きなリファクタリングPR#48149がマージされたようです:)
/ cc me
次へ@ varkorPR #51880
constジェネリックの進歩について質問している人がいることを知っているので、簡単な更新を提供したかっただけです。
いくつかのコンテキストを与えるために、3月に@yodaldevoidと私はほとんど機能していた最初の実装を持っていました(実装の大部分は完了したようで、残りのクラッシュをクリーンアップしていました)。 しかし、私たちが取り組んでいたブランチはプレミリでした。 miriがマージされたとき(そしてその後の多くのPRで)、一定の評価を処理するためのコードが大幅に変更されました。つまり、私たちが行ったことの多くが時代遅れになりました。
その上、読みやすさを向上させるだけでなく、特定のケースでconstジェネリックを処理するのを忘れた場合にミスを犯すために、constジェネリックを追加する前にジェネリックパラメーターコードを一般的にクリーンアップする必要があることが決定されました。作る。 これは、で行われたhttps://github.com/rust-lang/rust/pull/48523 、 https://github.com/rust-lang/rust/pull/48149とに完成予定のhttps:// githubの。 com / rust-lang / rust / pull / 51880。 これらは私が当初予想していたよりも少し複雑で、予想よりもプッシュスルーに少し時間がかかりました。
それまでの間、 @ yodaldevoidと私は、元の実装をその後のrustcのすべての変更と互換性のあるものにするために取り組んできました。 しばらく時間がかかりましたが、私たちはそこに到達しています(ただし、期待したほど多くの時間がないという長年の問題があります)。 その面ですぐに良いニュースがあることを願っています。 (その間、 https://github.com/rust-lang-nursery/chalkは順調に進んでおり、 @ eddybが最初に説明したいくつかの問題に対処する必要があります。)
何人かの人々は彼らがどのように助けることができるかを尋ねました:私はこの段階で、私たちが最初の実装を終えて、次にどの部分に注意が必要かを見ることを試みるのがより簡単になると思います。 準備ができたら、さまざまな場所でconstジェネリックを利用して(エラーメッセージ用に無効なものを含む)、多くのテストが必要になるので、それは間違いなく私たちが多くの助けを借りてできる場所です! それが起こったらお知らせします!
そのための適切な場所ではない場合は申し訳ありませんが、抽象的なconst式の同等性について提案があります。 一般に、それは完全に依存する型付けと決定不可能な領域に直接還元されます。 ただし、さびた状態では、すべてが最終的に具体的な値/タイプでインスタンス化されるため、いくつかの同等性を主張し、それらのチェックを単相インスタンスに延期することができます。
私が言いたいのは、抽象const式の同等性をチェックする比較的簡単な方法は次のとおりです。
N+1 == N+1
はそのままで機能するはずです)N+M == M+N
などの方程式を追加できるようにします(おそらくwhere
句にありますか?)。 これらの方程式は、(何らかの形の合同クロージャーを使用して)等価性チェックで使用できます。 これらの提供された方程式を使用してタイプチェックを行わない定義は拒否されます。false
に減少する場合、コンパイルエラーが発生するか(「方程式は公理です」)、特性をインスタンス化できません(「方程式は制約です」)。N
によってパラメータ化された関数f
N
がある場合、 N+0==N
場合、この方程式は呼び出し元g
失われてはなりません。 g
が呼び出される各単相の場所でチェックする必要があるためです。この方法の長所は、rustc自体に定理証明者、SMTソルバー、または算術リライターが必要ないことです。 タイプ、モジュロエイリアス、およびユーザーが提供する一連の方程式のモジュロの構文的同等性チェックのみを「のみ」行います。
短所は、 M+N=N+M
ような一見明らかな同等性を明示的に追加する必要があるため、ユーザーにとってより手動であるということです。
/// this works directly because of syntactic checks
fn add_end<T:Copy, const N: usize>(a: &[T;N], b: T) -> [T;N+1] {
let x : [T;N+1] = [b;N+1];
x[0..N] = a;
x
}
/// this should work already
fn append<T:Copy, const M:usize, const N:usize>(a: &[T;M], b: &[T;N])
-> [T;{M+N}]
{ … }
/// Here the equation M+M==N must be carried over or checked whenever this function is used
fn sum_pairwise_append<const M: usize, const N: usize>(a: &[i32, M],b: &[i32;N])-> [T;N]
where M+M == N {
let mut res : [i32; N] = append(a,a);
for i in 0 .. N { res[i] += b[i] };
res
}
fn main() {
let a: [i32; 2] = [1;2];
let b: [i32; 4] = [2;4];
let _ = sum_pairwise_append(a, b); // need to check 2+2=4
}
つまり、 e1 == e2
は常に真である必要があるため、実際にはwhere
制約ではなく、タイプチェッカーで使用できるassert_eq!(e1,e2)
ようなものです。 ここで、実装者はこれが常に真であると約束し、方程式に反論するパラメーターをユーザーが提供すると、ユーザーをコンパイルエラーにさらします。
ここで、節where e1 == e2
は、このパラメーター化された特性/関数を正常に使用するために満たす必要のある制約です。 これは、 e1 == e2
が常に成立する必要がないことを意味します。これは、 z==0
インスタンス化に失敗する(x*y) / z == (y*x) / z
など、ドメイン全体でのみ真の方程式で興味深い場合があります。
@ c-cube「 WF
(整形式)ルール」から派生した、少なくとも関数については、「暗黙のwhere
句」のシステムがすでにあります。つまり、 fn foo<'a, 'b>(x: &'a &'b i32) {}
を定義する場合です。 'b: 'a
を満たす必要があります( WF(&'a &'b i32)
-> &'b i32: 'a
-> 'b: 'a
が必要な結果)。
そのシステムを再利用して(実際にはこれはすでに実装されています) 、署名によって与えられた制約(「この定数式は正常に評価されます」の形式)を呼び出し元にwhere
が必要です。
あなたが説明している他のほとんどすべては計画されたものに近いようです(「構文の同等性」の形式を含む)が、「単形化時間」チェックは必要ありません。代わりに方法をとることができます(暗黙的またはwhere
介して) または制約を評価できて保持されない場合にエラーが発生します。
あなたが説明している他のほとんどすべては、計画されているものに近いようです
@eddybこれらの計画について説明している参考記事を知っていますか?
@ flip111おそらくRFCのいくつか。 たぶん@withoutboatsはもっとよく知っています。
#51880 is done :tada: :tada:
@withoutboats @ miri
でシンボリック評価の基本的な形式を取得する際に、 N + 1
(たとえば[T; N + 1]
)のような式の適切な統合をブロックすることについてどう思いますか?
@varkorがConstValue::{Infer,Param}
追加すると、それをエンコードするメカニズムがすでにあると思います。これを使用して、「(浅く)有効だが不明な」(シンボリック)値を追跡し、値(主に)を含めることができます。整数)その上での操作と呼び出し。
assert
制御フローの決定には、既知の値が必要ですが、 assert
自体はおそらくAssertThen(condition, assert message, success value)
として修正できます。
( condition
とsuccess value
両方が象徴的である可能性があります!)
シンボリック値をty::Const
にエクスポートできるということは、(ほとんどの?)操作を不透明として扱い、miriの外部で統合の一部を実装できることを意味します。
推論変数を想定しないように注意する必要がありますが、たとえばN + 1
場合、2つのAdd(Param(N), Bits(1))
統合することをサポートできると思います。
@varkor私が思うテストケースは、統一せずに、怠惰な正規化だけで機能するはずです。
/*const*/ fn append<const N: usize, T>(xs: [T; N - 1], x: T) -> [T; N] {
let new = MaybeUninit::<[T; N]>::uninitialized();
unsafe {
let p = new.as_mut_ptr() as *mut T;
(p as *mut _).write(xs);
p.add(N - 1).write(x);
}
new.into_inner()
}
それは次の理由でのみ機能します:
N - 1
はタイプレベルで1回だけ表示されますtype ArrayWithoutLast<T, const N: usize> = [T; N - 1];
)N
具体的な値を提供することにより、それがWFであることを証明できます。ArrayWithoutLast
where
句に埋め込む」必要があります(例: [T; N - 1]: Sized
where [T; N - 1]:
悪用することさえできます(今日は無視されますか?痛いです!)where ArrayWithoutLast<T, N>: ...
との統合をバイパスしますしたがって、全体として、おそらくWFルールを使用してconst式の「検証」をユーザーに強制することができますが、それでも他のこと( ArrayWithoutLast
ようなハックを必要としないことを含む)の統合が必要です。
測定単位の議論から生じる小さな質問。 定数よりもジェネリック型が直接依存しない場合はどうすればよいですか? 例えば:
struct Foo<T, const N: usize> {
a: T,
// Will such array be "the way" to handle this problem?
// Or do we want some kind of `std:marker::PhantomConstData`? (which can be a simple
// type alias to the same array)
// Or maybe make `PhantomData` to accept constants?
_phantom: [(), N],
}
_ファントム:[()、N]、
[(); N]
を意味していると思いますが、これはusize
のみ機能します。
私はそれを持っている感覚になるだろうと思う特別なmarker::PhantomConst
任意の可能const
タイプを。
うーん…最新のコメントを念頭に置いてRFCを読み直して、私は疑問に思っています:このようなことは許可されますか?
struct Foo<T, const V: T> {
_phantom: PhantomConst<T, V>,
}
私はそれがどこでも禁止されているのを見ることができませんが、少なくとも例に値すると思ったでしょう。
@Ekleog :私が知る限り、constパラメーターの型は最初は型パラメーターに依存しないかもしれません(拡張には間違いなく意味がありますが)。 (これを許可すると実装が難しくなるため、最も単純なバージョンから始めるのが理にかなっています。)
これの進捗状況はどうですか? これが毎晩ヒットするおおよその時間を知っていますか?
@Zauberklavierこのプルリクエストが完了するとすぐに。 それから引用するには:
行くには長い道のりがあります
@newpavlovフィールドで使用せずに定数パラメーターが許可されると想定しました。
タイプパラメータを使用する必要がある理由は分散のためですが、定数にはこの問題はありません。
@eddybは、RFCの議論からも思い出します。
@varkorつまり、 @ cuviperによって提案されたPhantomConst
は、このRFCの現在の状態では存在できないということです…そうですか? 最新のコメントは、とにかくPhantomConst
の必要がないことを指摘しているようですが。
constパラメーターは、関連するタイプパラメーターの分散に影響を与えますか?
現在、constジェネリックはその型に型パラメーターを持つことができないと思います。
これの最初の動作バージョンで何ができるかはわかりません。
たとえば、このコメントのコードがコンパイルされるかどうか。
https://github.com/rust-lang/rfcs/pull/2581#discussion_r230043717
そのコードがコンパイルされた場合、構文を取得する前に定数の制約を適用する方法になります。
@ rodrimati1992おそらく、別のタイプがなくても(): IsTrue<{N < 128}>
できます。
制約を再利用して実際に機能させたいと思いますか? ( N < 128
式をコピーしても、最初は機能しないため)
trait Lt128<const N: usize> = IsTrue<{N < 128}>;
使用できると思います。
where [(); 128 - N],
書くだけのオプションもあると思います(しかし、それがパニックになることを保証するかどうかはわかりません)。
@ rodrimati1992おそらく、別のタイプがなくても
(): IsTrue<{N < 128}>
できます。
制約を再利用して実際に機能させたいと思いますか? (N < 128
式をコピーしても、最初は機能しないため)
trait Lt128<const N: usize> = IsTrue<{N < 128}>;
使用できると思います。
where [(); 128 - N],
書くだけのオプションもあると思います(しかし、それがパニックになることを保証するかどうかはわかりません)。
だから、私が書き直すことができる特性エイリアスでこれになりますか?:
trait AssertLessThan128<const N:usize>=
Assert<{N<=128}, (
Str<"uint cannot be constructed with a size larger than 128,the passed size is:",
Usize<N>>
) >;
アサートのアイデア
(Str<"uint cannot be constructed with a size larger than 128,the passed size is:",Usize<N>>)
これは、コンパイル時エラーが発生した理由をエラーメッセージで示しているため、 where [(); 128 - N],
よりも役立つと思います。
定数でif !(N < 128) { panic!("...") }
を実行することもできますか?
const trait
制約(または同様のもの)が到着するまで、std :: fmtアーキテクチャは存在しないと思いましたか?
これにより、複数の値(タイプに埋め込まれている)を含むエラーメッセージを表示できます。
実行可能な例を表示する方が簡単なので、constジェネリックがこれについて話すのを待つ方が良いかもしれません。
@ rodrimati1992 https://github.com/rust-lang/rfcs/blob/master/text/2345-const-panic.mdを参照して。const panic!()
を取得する前に特別なケースがあります。他の機能を介して可能です(おそらく、最初は値のカスタムフォーマットを許可しないでしょう)。
@ rodrimati1992ああ、なるほど、それは確かに巧妙なトリックです!
これの状況はどうですか?
これは正しいです。 PR#53645に従わない人のために迅速な更新を提供するために、constジェネリックは少なくとも1つの単純なユースケースでコンパイルおよび動作します。 残っているのは、arrysのconstジェネリックスや、エラー出力のクリーンアップなど、他のユースケースのcodegenを完成させることです。 その後、PRはマージの準備ができているはずであり、人々はそれで遊び始めることができます。
トピックから外れているかもしれませんが、これにより、チャンクのバリアントまたはフォークと関連するメソッドで、 Item
スライスではなくコンパイル時の固定サイズの配列にすることができますか? これにより、 for
ループが、チャンク内の要素を現在失敗しているfor (first, second) in arr.chunks(2)
などの変数にマップする、反駁できないパターンに分解/バインドできるようになります。確かに)特定のユースケースでより多くの最適化が可能になること。
あなたが考えるかもしれない@jeffvandyke ChunksExact特に。 このような変更はAPIを壊すものになるため、実行できません。 将来、配列参照を提供する新しいAPIを追加することはできますが、既存のAPIを壊すことはできません。
あなたが考えるかもしれない@jeffvandyke ChunksExact特に。 このような変更はAPIを壊すものになるため、実行できません。 将来、配列参照を提供する新しいAPIを追加することはできますが、既存のAPIを壊すことはできません。
ChunksExact
とそのバリアントに加えてそのようなAPIを追加するPRを提案する前に、これが実装されて安定するのを待っているだけです:)
ランタイムバリアントとコンパイル時バリアントの両方にユースケースがあり、チャンクサイズを事前に常に把握しているとは限りません。 最適化に関しては、定数を指定してChunksExact
を使用する場合、私のテストによれば、ほぼ同じになるはずです。 コンパイラは、すべての境界チェックを最適化できます。
実施され、安定化される
このようなAPIは、安定化のを実行するのに役立つもう1つの良い使用法であるため、安定化を待たないことをお勧めします。
これはimpl
ブロックではまだ機能しないと思いますか? 私は試した
#![feature(const_generics)]
struct The<const Val: u64>();
impl<const Val: u64> The<Val> {
fn the() {
println!("{}", Val);
}
}
しかし、実際に警告されたように、コンパイラは次のようにクラッシュしました
thread 'rustc' panicked at 'slice index starts at 1 but ends at 0', src/libcore/slice/mod.rs:2419:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
stack backtrace:
0: std::sys::unix::backtrace::tracing::imp::unwind_backtrace
1: std::sys_common::backtrace::_print
2: std::panicking::default_hook::{{closure}}
3: std::panicking::default_hook
4: rustc::util::common::panic_hook
5: std::panicking::rust_panic_with_hook
6: std::panicking::continue_panic_fmt
7: rust_begin_unwind
8: core::panicking::panic_fmt
9: core::slice::slice_index_order_fail
10: rustc_resolve::Resolver::resolve_ident_in_lexical_scope
11: rustc_resolve::Resolver::resolve_path
12: rustc_resolve::Resolver::resolve_path_without_parent_scope
13: rustc_resolve::Resolver::smart_resolve_path_fragment
14: rustc_resolve::Resolver::smart_resolve_path
15: <rustc_resolve::Resolver<'a> as syntax::visit::Visitor<'tcx>>::visit_ty
16: syntax::visit::walk_generic_args
17: syntax::visit::walk_ty
18: rustc_resolve::Resolver::with_generic_param_rib
19: rustc_resolve::Resolver::resolve_item
20: rustc_resolve::Resolver::resolve_crate
21: rustc::util::common::time
22: rustc_interface::passes::configure_and_expand_inner
23: rustc_interface::passes::configure_and_expand::{{closure}}
24: <rustc_data_structures::box_region::PinnedGenerator<I, A, R>>::new
25: rustc_interface::passes::configure_and_expand
26: <rustc_interface::queries::Query<T>>::compute
27: <rustc_interface::queries::Query<T>>::compute
28: <rustc_interface::queries::Query<T>>::compute
29: rustc_interface::queries::<impl rustc_interface::interface::Compiler>::prepare_outputs
30: rustc_interface::interface::run_compiler_in_existing_thread_pool
31: <std::thread::local::LocalKey<T>>::with
32: <scoped_tls::ScopedKey<T>>::set
33: syntax::with_globals
このエラーは、 impl<const Val: u64>
のconst
に関連しています。これは、その部分を削除すると他のエラーが発生しますが、クラッシュしないためです。
しかし、この機能を検討することさえ、Rustに支持します。 それが機能するかどうかはわかりませんでしたが、構文は自然に見えたので、私はそれを選びました、そしてlorustcはそれが存在すると言いました:)
この非常に期待されている機能は、私たちが話しているようにコンパイラーを介してまだ配管されているので、それが機能していないことは驚きではありません(たとえば、#59008と#58581、およびPRが大きすぎるために放棄された#53645の以前の作業を参照してください) 、ただし、進行状況を通知するためのトラッカーとして開いたままにします)。
ただし、現在の実装スタブで範囲外のスライスアクセスが予想されるかどうかはわかりません。 @varkor @yodaldevoid 、ご覧いただけますか?
はい、警告は正しいです:constジェネリックはまだどのような形でも機能していません。 試してみる準備が整う前に、まだいくつかのプルリクエストがあります。
これが質問をするのに適切な場所ではない場合は申し訳ありませんが、これ以上の場所は見つかりませんでした。 たった2つの質問:
単一の関数を条件付きで一定にすることはできますか? たとえば、関数には次のような2つのシグネチャを含めることができます。
const fn foo<A: const T>(x: T) // `A` const implements `T`
fn foo<A: T>(x: A) // `A` implements `T` normally
この場合、 foo
はA: const T
場合は一定です。 A
がT
const実装しない場合でも、境界を満たしますが、 foo
はconstではなくなります。 また、作成者がより複雑な例の一般的な境界を指定できることも重要です(例: where A::Output : Bar
)。 この良い例は、単純な算術ですらあります。
// This only accepts types that const implement `T`
const fn square_const<T: const Mul>(x: T) -> T {
x*x
}
// This accepts types that implement `T` any way, but it is not const
// This function behaves identically to `square_const`
// But has a different signature and needs a different name
fn square<T: Mul>(x: T) -> T {
square_const(x)
}
let a: u8 = 5;
let b: FooNumber = FooNumber::new();
square_const(a); // `u8` const implements Mul
square(b); // `FooNumber` implements `Mul` normally, so we need a separate function?
これを行う方法は間違いなくあるはずだと強く感じており、RFCに記載されていないことに驚いています(見逃していない限り)。
_ [それほど重要ではありません]:_ const関数の本体で、コンパイル時と実行時のどちらで実行されているかを検出する方法はありますか? cfg!()
似たマクロは、デバッグ以外の理由がなければ、便利な拡張機能になると思います。 cfg!()
は現在、コンパイル時にすでに評価されているので、関数がconstとして使用されているのか、通常の関数として使用されているのかを知ることができるはずです。これもコンパイル時に決定されるからです。 -時間。 ただし、これは私の最初の質問ほど重要ではありません。
@ Coder-256
@ Coder-256これらの質問は両方とも、constジェネリックではなく、const関数に関連しています。 constジェネリックは、コンパイル時に実行できる関数ではなく、const( foo<2>()
)よりもジェネリックであるためのものです。 これが、RFC2000で質問に対する回答が見つからなかった理由だと思います。
@rpjohnstありがとうございますが、条件付きでconst関数です。 説明するのが難しいので、私が書いた例を参照してください。
@yodaldevoidおっと、あなたは正しいです、私はこれをどこに尋ねるべきですか?
マクロの質問については、考えてみるとあまり役に立たないということに同意します。
私が求めているのは、条件付きでconst関数です。 説明するのが難しいので、私が書いた例を参照してください。
square_const
定義は、 square
代わりに使用できます(つまり、実行時に同等のシグネチャを持つ関数に強制変換されます)。 この動作の説明については、 https://github.com/rust-lang/rfcs/pull/2632を参照してconst fn
と特性境界の間の相互作用について質問するのに適した場所です)。
@varkor (特性の境界が変わるため)そうだとは思いませんが、rust-lang / rfcs#2632で質問します。
クラッシュレポート:
コード:
#![feature(const_generics)]
use std::marker::PhantomData;
struct BoundedU32<const LOWER: u32, const UPPER: u32> {
value: u32,
_marker: PhantomData<(LOWER, UPPER)>,
}
コンパイラ:
thread 'rustc' panicked at 'slice index starts at 1 but ends at 0', src/libcore/slice/mod.rs:2419:5
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
error: internal compiler error: unexpected panic
note: the compiler unexpectedly panicked. this is a bug.
note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports
note: rustc 1.35.0-nightly (719b0d984 2019-03-13) running on x86_64-unknown-linux-gnu
note: compiler flags: -C codegen-units=1 -C debuginfo=2 --crate-type lib
note: some of the compiler flags provided by cargo are hidden
error: Could not compile `playground`.
To learn more, run the command again with --verbose.
@Jezza :constジェネリックはまだ完全には実装されておらず、機能することは期待されていません。 #![feature(const_generics)]
実験を開始する時期が来たら、お知らせします(この問題を購読している場合は通知されます)。
@varkorちょっと待ってください、 @ Jezzaの例には_marker: PhantomData<(LOWER, UPPER)>
-これらはタイプとして使用される2つのconst
パラメータですが、なぜrustc_resolve
でエラーが発生しなかったのですか?
良い点:これを調査します。 これは#![feature(const_generics)]
問題にすぎないため、重大な問題ではないことに注意してください(非ジェネリック定数を使用すると、期待どおりにエラーが発生します)。 多分それはrustc_resolve
に決して達しないでしょう。
〜 @ eddyb :このICEはresolve_ident_in_lexical_scope
から来ているので、おそらくhttps://github.com/rust-lang/rust/issues/58307に関連していると思います
編集:実際には、そうではないかもしれません—それはmacro_rules!
だけに当てはまるようです。
最小化:
#![feature(const_generics)]
struct S<const C: u8>(C);
「予期されたタイプ、検出された値」エラーを生成する前に、ICEを解決してください。
インデックスはここでは範囲外です:
https://github.com/rust-lang/rust/blob/master/src/librustc_resolve/lib.rs#L3919
現在のnightlyは、次のコードに対して「パラメーターN
は使用されません」を生成します。
struct Foo<const N: usize> { }
前の議論から、コンパイルはこのコードを受け入れるべきだと思いました。 それは単に未完成の実装の成果物ですか?
@newpavlovの通常のタイプのパラメーターを使用する必要がありますが、 PhantomData<n>
を実行できる必要がありますか?
PhantomData<[(); N]>
を実行することはすでに可能だと思います。 ただし、AFAIUの場合、 PhantomData
のポイントは分散をマークすることであり、AFAIUには分散の概念がないため、これを実際に適用したいのかどうかはわかりません。 constジェネリックパラメーター。
そして、それはNがタイプusize
場合にのみ機能します。
RFCの議論中に、constパラメータを使用する必要はなく、現在の実装はバグであると判断しました。
@withoutboatsRFCのどこに記載されているかを指摘できますか? 私はその趣旨の何かを見つけようとしましたが、それを見逃したに違いありません。
RFCテキストに含まれていたかどうかわかりません
@withoutboatsそれがどこで議論されたかを指摘して
@yodaldevoidこのコメントは以前に参照されました: https ://github.com/rust-lang/rust/issues/44580#issuecomment-419576947。 しかし、それはRFC PRにはなく、むしろこの問題に関するものでした。
数年前のコメント履歴を調べることはできませんが、説明できます。型変数を使用して、これらのパラメーターの分散について考えさせるためのブロッカーとして必要です(IMOもこれは不要であり、デフォルトで共変に設定できます。しかし、それは別の問題です)。 Constパラメーターには分散との相互作用がないため、これには動機がありません。
@ HadrienG2関連するコメントを見つけていただきありがとうございます。
@withoutboats私は、あなたがこれについての長年のコメントのすべてをトロールすることを本当に期待していませんでした、それはあなたがすでにそれを手にしたという希望だけでした。
ご説明ありがとうございます。 何度学習しようとしても、分散に頭を悩ませることはできないことを認めなければなりませんが、それがなくても、コストパラメータを使用する必要がないことをもう一度考えた場合は理にかなっています。 このバグの修正をFIXMEのリストに追加します。
これは、constジェネリックのこれまでの進捗状況の要約です。
constジェネリックの作業を適切に開始する前に、実行する必要のあるリファクタリングがいくつかありました。 @jplatteはhttps://github.com/rust-lang/rust/pull/45930でタスクを引き受けました@ jplatteはconstジェネリックのメイン実装の作業を
@yodaldevoidと私は、 @ jplatteがhttps : //github.com/rust-lang/rust/pull / 48452 、 https://github.com/rust-lang/rust/pull/48523、https: //github.com/rust-lang/rust/pull/51880。
これが完了すると、constジェネリックの実装が本格的に開始される可能性があります。 それ以来、 @ yodaldevoidと私はゆっくりと、しかし確実にconstジェネリックのサポートを段階的に追加してきました: https : //github.com/rust-lang/rust / pull / 58503 、 https : //github.com/rust-lang/rust/pull/58581、https://github.com/rust-lang/rust/pull/58583、https://github.com/rust -lang /錆/プル/ 59170 、 https://github.com/rust-lang/rust/pull/59355 、 https://github.com/rust-lang/rust/pull/59415 、 https://でgithubの.com / rust-lang / rust / pull / 60058 、 https ://github.com/rust-lang/rust/pull/60280、https://github.com/rust-lang/rust/pull/60284およびほとんど最近https://github.com/rust-lang/rust/pull/59008。 (これらは主に、メインのconstジェネリックプルリクエストから分割され
今の状況は? 一部のconstジェネリックテストが機能するようになりました🎉ただし、機能しないテストもあり、コード全体でまだ対処する必要のあるFIXME
がいくつかあります。 しかし、私たちは今近づいています、そしてあなたが助けたいのであれば、私たちはいくつかの手に負えない果物がある段階にあります。
FIXME(const_generics)
散在しています。 私たちはそれらを処理することを計画していますが、 @ yodaldevoidと私には時間があまりないので、1つに取り組むことができると思う場合は、先に進んでください(コメントを残したいので、私たちはしません) t重複した努力)。constジェネリックが適切なテストの準備が整う前に、残りの実装の問題のいくつかの概要をトップの投稿に書き、何をすべきかについての大まかなアイデアを提供しました。
時間はかかりますが、着実に進んでおり、今後もペースを上げていきたいと考えています。 物事がうまくいき始めているのをようやく見るのはやる気を起こさせます!
私はこの投稿に何ヶ月も期待していたFIXME
の1つに取り組みたいです
@ jplatte 、 @ yodaldevoid 、 @ varkorおめでとうございます。 これは、数学ライブラリのカスタムArray-like
特性を取り除くための大きなステップです。
クロスポスト...
@varkorテストに関しては、何が機能すると予想されるかを知るのに役立つかもしれません。 たとえば、これが機能しないことに驚きました: https :
また、FIXMEに関心のある方は、https://oli-obk.github.io/fixmeh/を参照してください。
@ mark-im {}
をFOO
周りに配置してみてください。 そうすればそれは機能します。
https://github.com/rust-lang/rfcs/blob/master/text/2000-const-generics.mdの引用:
アイデンティティ式ではない式をconstパラメータ(配列を除く)として適用する場合、式はブロック内に含まれている必要があります。 この構文上の制限は、型内の式を解析するときに無限の先読みを必要としないようにするために必要です。
{expression}
は、式が識別子でもリテラルでもない場合にのみ必要です。これはバグです。
同じRFCを引用する:
アイデンティティ式:スコープ内の名前に置き換える以外に評価できない式。 これには、すべてのリテラルとすべてのIDが含まれます(例:3、 "Hello、world"、foo_bar)。
@ mark-imはい、現在、最初に解析するときに、型と定数のIDの違いを区別することはできません。 私たちは、これらすべてを将来的にどのように処理するかについての決定を急いで行いました。 今は未来かもしれないと思います。
少し背景を説明するために、これを処理する方法について覚えておくことができる2つの方法について説明しました。 最初の方法は、人々にconst引数をマークするように強制することです(前にconst
入力するか、他の方法で)。 これは人間工学の観点からは素晴らしいことではありませんが、構文解析の観点からは簡単です。 2番目の方法は、コンパイル中に後でジェネリックパラメーターとペアリングを開始するまで、すべてのジェネリック引数を同じものとして扱うことです。 これはおそらく私たちが採用したい方法であり、これを可能にするためのいくつかの作業がすでに行われていますが、私たちは最終的な飛躍を遂げていません。
素晴らしい仕事。 可能であれば、 FIXME
のいくつかを修正して、喜んでお手伝いさせていただきます。 rustcコードベース内での経験はまったくないので、FIXMEhttps://github.com/rust-lang/rust/blob/master/src/librustc_mir/monomorphize/item.rs#から始めます。 L397 、それは簡単なもののように思われるので。
次のコードはどうですか?
trait Foo {
const N: usize;
fn foo() -> [u8; Self::N];
}
現在、「現在のスコープでタイプSelf
N
という名前の関連アイテムが見つかりません」というコンパイルエラーが発生します。 そのようなコードはFIXME
終了した後に受け入れられますか、それとも実装するために追加の努力が必要ですか?
@yodaldevoid
簡単な質問です。これがすでに議論されている場合はお詫びします。
2番目の方法は、コンパイル中に後でジェネリックパラメーターとペアリングを開始するまで、すべてのジェネリック引数を同じものとして扱うことです。 これはおそらく私たちが採用したい方法であり、これを可能にするためのいくつかの作業がすでに行われていますが、私たちは最終的な飛躍を遂げていません。
これは、関数の実装に関連するエラーの生成を回避するために、Rustが関数のシグネチャを明示的にするという原則に反するものではありませんか? おそらく私はこれを完全に誤解していて、あなたは関数の本体内のジェネリックパラメーターを解析することについて話しているのでしょう。
これは、関数の実装に関連するエラーの生成を回避するために、Rustが関数のシグネチャを明示的にするという原則に反するものではありませんか? おそらく私はこれを完全に誤解していて、あなたは関数の本体内のジェネリックパラメーターを解析することについて話しているのでしょう。
これは、ジェネリックパラメーターとして関数に渡される識別子が定数であるか型であるかを判断することです。
例:
fn greet<const NAME:&'static str>(){
println!("Hello, {}.",NAME);
}
const HIS_NAME:&'static str="John";
greet::<HIS_NAME>();
greet::<"Dave">();
関数を定義するときは、NAMEが定数であることを指定する必要がありますが、それを呼び出すときは、HIS_NAMEがturbofish演算子( ::< >
)内で定数であると言う必要はありません。
@zestererこの提案されたソリューションがユーザーに対して透過的にされた場合(たとえば、関数を定義するときにパラメータータイプ間に違いがなかった場合)、あなたは正しいでしょう。 ただし、提案されたソリューションの背後にある考え方は、ユーザーが直面する動作を変更することではなく、実装を変更することだけです。 ユーザーは、明示的なtype、const、lifetimeパラメーターを使用して関数シグネチャを書き出すことになり、ジェネリック引数は対応するパラメーターと一致する必要があります。コンパイラーでの解析方法が変わるだけです。
@newpavlovコードが機能しないことに驚いています。 別の号を提出していただければ幸いです。
@yodaldevoid @robarnold
ああ、わかった。 これは型/関数のシグネチャに関連していると誤解しました。
組み込み配列型でconstジェネリックを使用するために、#60466を開いて、これに関する他の人の意見を確認しました。
現時点では、Implブロックは完全に壊れているようです。 これは機能の現在の状態で予想されますか、それとも別の問題を開く必要がありますか?
現時点では、Implブロックは完全に壊れているようです。 これは機能の現在の状態で予想されますか、それとも別の問題を開く必要がありますか?
N
、 impl<const N: usize> Dummy<{N}> {}
の周りに{}
を追加する必要があります。
しかし、そうすると、制約のないパラメーターについてエラーが発生します。
ああ、ありがとう。 中括弧のことを忘れました!
これは非常に最小化されたテストケースだったので、それが解決されると失敗するのは驚きではありません。
...しかし、ええ、これはおそらく機能するはずです:
#![feature(const_generics)]
trait Dummy {}
struct Vector<const N: usize> {
data: [f32; N],
}
impl<const N: usize> Dummy for Vector<{N}> {}
...それでもE0207では失敗します:
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
--> src/lib.rs:1:12
|
1 | #![feature(const_generics)]
| ^^^^^^^^^^^^^^
error[E0207]: the const parameter `N` is not constrained by the impl trait, self type, or predicates
--> src/lib.rs:9:12
|
9 | impl<const N: usize> Dummy for Vector<{N}> {}
| ^ unconstrained const parameter
error: aborting due to previous error
For more information about this error, try `rustc --explain E0207`.
error: Could not compile `small-matrix`.
To learn more, run the command again with --verbose.
...そしてこれが@varkorが#60466の「constsジェネリックの配列処理に関する問題」が意味するものだと思います。
#![feature(const_generics)]
fn dummy<const N: usize>() -> [f32; N] {
[0.; N]
}
->
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
--> src/lib.rs:1:12
|
1 | #![feature(const_generics)]
| ^^^^^^^^^^^^^^
error: internal compiler error: cat_expr Errd
--> src/lib.rs:3:44
|
3 | pub fn dummy<const N: usize>() -> [f32; N] {
| ____________________________________________^
4 | | [0.; N]
5 | | }
| |_^
error: internal compiler error: cat_expr Errd
--> src/lib.rs:4:5
|
4 | [0.; N]
| ^^^^^^^
error: internal compiler error: QualifyAndPromoteConstants: Mir had errors
--> src/lib.rs:3:1
|
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | | [0.; N]
5 | | }
| |_^
error: internal compiler error: broken MIR in DefId(0/0:3 ~ small_matrix[5b35]::dummy[0]) ("return type"): bad type [type error]
--> src/lib.rs:3:1
|
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | | [0.; N]
5 | | }
| |_^
error: internal compiler error: broken MIR in DefId(0/0:3 ~ small_matrix[5b35]::dummy[0]) (LocalDecl { mutability: Mut, is_user_variable: None, internal: false, is_block_tail: None, ty: [type error], user_ty: UserTypeProjections { contents: [] }, name: None, source_info: SourceInfo { span: src/lib.rs:3:1: 5:2, scope: scope[0] }, visibility_scope: scope[0] }): bad type [type error]
--> src/lib.rs:3:1
|
3 | / pub fn dummy<const N: usize>() -> [f32; N] {
4 | | [0.; N]
5 | | }
| |_^
thread 'rustc' panicked at 'no errors encountered even though `delay_span_bug` issued', src/librustc_errors/lib.rs:356:17
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.
error: internal compiler error: unexpected panic
note: the compiler unexpectedly panicked. this is a bug.
note: we would appreciate a bug report: https://github.com/rust-lang/rust/blob/master/CONTRIBUTING.md#bug-reports
note: rustc 1.36.0-nightly (cfdc84a00 2019-05-07) running on x86_64-unknown-linux-gnu
note: compiler flags: -C debuginfo=2 -C incremental --crate-type lib
note: some of the compiler flags provided by cargo are hidden
@ HadrienG2
#60619および#60632を参照してください。
私はこのコードが構築されるべきだと思います:
https://play.rust-lang.org/?version=nightly&mode=debug&edition=2018&gist=e45b7b5e881732ad80b7015fc2d3795c
しかし、それは現在エラーです:
エラー[E0119]:タイプMyArray<_, _>
のトレイトstd::convert::TryFrom<[type error]>
実装の競合:
残念ながら、これはTry{From,Into}
制限であり、constジェネリックとは何の関係もありません。 多くの場合、一般的に実装することはできません: playpen
あなたの例の@oberienでは、 T
は外部タイプであり、 T: Into<Foo<T>>
が成立するかどうか
私の例では、[T; N]はlibcoreで定義された型であり、 #[fundamental]
(これが何を意味するのかさえわかりません)ので、トレイトリゾルバーは実際には[T; N]: Into<MyArray<T, {N}>>
が成り立たないことを知っていると思います。対立であってはならない、と私は思いますか?
[...]そして
#[fundamental]
[...]
これが問題です。 fundamental
は、ダウンストリームユーザーがローカルタイプを基本ラッパータイプでラップするときにトレイト実装を定義できるようにすることに関連しています。 この遊び場では、実装が非基本タイプ間で機能することがわかりますが、 from
タイプがfundamental
とマークされている場合は失敗します(はるかに優れたエラーメッセージが表示されます)。
@varkor @yodaldevoidつまり、 S<{N == 0}>
( S
がconst bool
パラメーターを取り、 N
がconst usize
をとる)という状況に陥りました。 S<{true}>
でもS<{false}>
でもありません(どちらとも同じ特性を実装していないという意味で)。これがバグなのか、現在のプロトタイプの予想される制限。 現在の統合ロジックについて簡単に復習していただけますか?
最小化された例として、これは...
// Machinery for type level if-then-else
struct Test<const B: bool>;
trait IfFn<S, Z> { type Out; }
impl<S, Z> IfFn<S, Z> for Test<{true }> { type Out = S; }
impl<S, Z> IfFn<S, Z> for Test<{false}> { type Out = Z; }
// Returns an u8 if B is true, else an u16
fn should_be_ok<const B: bool>() -> <Test<{B}> as IfFn<u8, u16>>::Out {
0
}
...次のエラーが発生します:
error[E0277]: the trait bound `Test<B>: IfFn<u8, u16>` is not satisfied
--> src/lib.rs:32:1
|
32 | / fn should_be_ok<const B: bool>() -> <Test<{B}> as IfFn<u8, u16>>::Out {
33 | | 0
34 | | }
| |_^ the trait `IfFn<u8, u16>` is not implemented for `Test<B>`
|
= help: the following implementations were found:
<Test<false> as IfFn<S, Z>>
<Test<true> as IfFn<S, Z>>
実際にはわかりませんが、問題は、 0
リテラルがu8
かu16
かを判別するロジックがコンパイラーにないことである可能性があります。 。 次のようなものを試してください: 0u8 as _
そうではありません、エラーメッセージは同じままです。
しかし、整数型推論を含まないバージョンが必要な場合は、次のような愚かな最小化があります。
struct Test<const B: bool>;
trait Not { const B: bool; }
impl Not for Test<{true }> { const B: bool = false; }
impl Not for Test<{false}> { const B: bool = true; }
fn should_be_ok<const B: bool>() -> bool {
<Test<{B}> as Not>::B
}
Test<{B}>
Not
実装していないため、まだ失敗しています。
やあ ! const値の統合が機能するかどうかはよくわかりませんが、作業中の新しい統合システム( "chalk")がありますが、rustcが現在使用しているものではありません。
したがって、あなたがやろうとしていることは、チョークの準備ができるまで待たなければならないかもしれません。 それでも、チョークでも機能するか、機能するはずかはわかりません。
進捗状況については、 https://github.com/rust-lang/rust/issues/48049を参照して
@ HadrienG2そのために別の問題を
@carado統合は
@ HadrienG2 constジェネリックに特化すると、次のようなことができるようになります。
struct Test<const B: bool>;
trait Not { const B: bool; }
impl<const B: bool> Not for Test<B> { const B: bool = false; }
impl Not for Test<{false}> { const B: bool = true; }
fn should_be_ok<const B: bool>() -> bool {
<Test<{B}> as Not>::B
}
あなたがしたいのは、形質システムに拡張されたばかりのマッチのためにあるような、ある種の徹底的なチェックです。 それがどのスレッドでも言及されていることを知りません。
@carado Chalkは、これが1つである実際の「型システムプリミティブ」には関係ありません。
つまり、 rustc
は(チョークがすでに部分的に統合されているため、今日でも)チョークに対して不透明なエンティティ(2つの異なる定数式など)を処理する統合の一部を実装する必要があります。
それを実装すれば(とにかく、近い将来の計画はありません)、チョークが特性システム(統合ではなくその主な目的)を置き換えた後でも、それはあまり変わりません。
ああ、それでは私の悪い! 何を言っているのかよくわからないと思います。
constジェネリックスが提供する素晴らしいものの1つは、コンパイル時に計算された単純な関数、たとえば階乗です。
例:
fn factorial<const X: i32>() -> Option<i32> {
match X {
i if i < 0 => None,
0 => Some(1),
1 => Some(1),
i => Some(factorial::<i - 1>().unwrap() + i)
}
}
私が理解している限り、夜間のconstジェネリックのサポートには制限がありますか? もしそうなら、私がそれを使用する方法などを見つけることができるこの問題よりも組織化された場所はありますか?
@ dancojocaru2000 const
関数は、コンパイル時の値レベルの計算に適した方法です。例: https : //play.rust-lang.org/?version = nightly&mode = debug&edition = 2018&gist = 4994b7ca9cda0bfc44f5359443431378まだ完全に実装されていないため、機能しません。 しかし、どちらもconst
ジェネリックではありません。
match
がconst
引数に対して機能するかどうかわからないため、スニペットに問題がある可能性があります。 また、コードで加算と乗算を混合しましたが、それはそれほど重要ではありません。
コンパイル時に、他の関数型言語のデモとしてよく表示されるPeanoエンコーディングを使用して階乗を実行できると思います。
コンパイル時の計算された単純な関数
より適切な機能はconstfnだと思います。
理論的には、コードはほぼそのまま機能します
#![feature(const_generics)]
fn factorial<const X: i32>() -> Option<i32> {
match X {
i if i < 0 => None,
0 => Some(1),
1 => Some(1),
i => Some(factorial::<{X - 1}>().unwrap() + i)
}
}
fn main() {
println!("{:?}", factorial::<10>());
}
しかし、そうではありません。
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
--> src/main.rs:1:12
|
1 | #![feature(const_generics)]
| ^^^^^^^^^^^^^^
error: internal compiler error: src/librustc_codegen_ssa/mir/operand.rs:79: unevaluated constant in `OperandRef::from_const`
本当に符号なしの型を使用する必要がありますが:
#![feature(const_generics)]
fn factorial<const X: u32>() -> u32 {
match X {
0 => 1,
1 => 1,
_ => factorial::<{ X - 1 }>() + X,
}
}
fn main() {
println!("{:?}", factorial::<10>());
}
この号よりも整理された場所で、使い方などを見つけることができますか?
それは通常不安定な本でカバーされ
Peanoエンコーディングを使用したコンパイル時の階乗、
その例については、 typenumcrateを参照してください。
理論的には、コードはほぼそのまま機能します
factorial::<0>
呼び出すとき、 _ => factorial::<{X - 1}>() * X
は正しくコード生成されますか? ただし、そうしようとすると、整数のアンダーフローが発生します。
このようなコードが将来コンパイルされることを期待できますか?
#![feature(const_generics)]
trait NeedsDrop<const B: bool> { }
impl<T> NeedsDrop<std::mem::needs_drop<T>()> for T { }
fn foo<D: NeedsDrop<false>>(d: D) { }
これは、constジェネリックとしてN <= 32
のいくつかの[T; N]
配列の最近の実装と交差していますが、次のコードは、さまざまなエラーで毎晩最新( 4bb6b4a5e 2019-07-11
)でコンパイルされませんthe trait `std::array::LengthAtMost32` is not implemented for `[u64; _]`
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct BigintRepresentation<
const N: usize
>(pub [u64; N])
where [u64; N]: std::array::LengthAtMost32,
[u64; N*2]: std::array::LengthAtMost32;
ただし、次のようになります。
#[derive(Copy, Clone, PartialEq, Eq)]
pub struct BigintRepresentation<
const N: usize
>(pub [u64; N])
where [u64; N]: std::array::LengthAtMost32;
N*2
は、そのような制約の有効な定数または修飾子ではないようです
@shamatar N*2
は、 `[u64; {N * 2}]
「トレイトが実装されていない」エラーは、派生マクロが[u64; {N*2}]
の境界を追加していないことに起因すると思いますが、派生が削除された場合、現在ICEがあります。
`` `エラー:内部コンパイラエラー:型の定数に無視されたエラーがありました:TooGeneric
-> src / main.rs:5:1
|
5 | / pub struct BigintRepresentation <
6 | | const N:使用する
7 | | >(pub [u64; N])
8 | | ここで[u64; N]:std :: array :: LengthAtMost32、
9 | | [u64; {N * 2}]:std :: array :: LengthAtMost32;
| | ____________________________________________ ^
スレッド 'rustc'は ' delay_span_bug
発行されてもエラーは発生しません'、src / librustc_errors / lib.rs:366:17でパニックになりました
注:バックトレースを表示するには、 RUST_BACKTRACE=1
環境変数を指定して実行します。
このコードはコンパイルされません:
#![feature(const_generics)]
struct Foo<const X: usize>([u8; X]);
impl<const X: usize> Foo<X> {
fn new() -> Self {
Self([0u8; X])
}
}
warning: the feature `const_generics` is incomplete and may cause the compiler to crash
--> src/lib.rs:1:12
|
1 | #![feature(const_generics)]
| ^^^^^^^^^^^^^^
error[E0573]: expected type, found const parameter `X`
--> src/lib.rs:5:26
|
5 | impl<const X: usize> Foo<X> {
| ^
| |
| not a type
| help: try using the variant's enum: `regex_syntax::ast::HexLiteralKind`
error[E0107]: wrong number of const arguments: expected 1, found 0
--> src/lib.rs:5:22
|
5 | impl<const X: usize> Foo<X> {
| ^^^^^^ expected 1 const argument
error[E0107]: wrong number of type arguments: expected 0, found 1
--> src/lib.rs:5:26
|
5 | impl<const X: usize> Foo<X> {
| ^ unexpected type argument
error: aborting due to 3 previous errors
@npmccallumあなたはFoo<{X}>
をする必要があります
@ pengowen123はい、 derive
なしでコンパイル( constant in type had an ignored error: TooGeneric
コンパイラをクラッシュさせます)しますが、そのような「二重制約」が事実上N <= 32
かどうかは別の質問かもしれません。 N <= 16
はコンパイラによって適用されません。
パラメータに言及している式はおそらくしばらくは機能しません。 [T; N]
とFoo<{N}>
は特殊なケースであり、 N
に言及している式は含まれていません。より大きな問題をバイパスして、 N
パラメータを直接参照します。
Self
を使用すると、クラッシュが発生します。
Value<{C}>
に交換しても、クラッシュします。
#![feature(const_generics)]
struct Value<const C: usize>;
impl<const C: usize> Value<{C}> {
pub fn new() -> Self {
unimplemented!()
}
}
pub fn main() {
let value = Value::new();
}
#61338と同じように、問題の原因はインクリメンタルコンパイルです。
コンパイル時の計算された単純な関数
より適切な機能はconstfnだと思います。
理論的には、コードはほぼそのまま機能します
#![feature(const_generics)] fn factorial<const X: i32>() -> Option<i32> { match X { i if i < 0 => None, 0 => Some(1), 1 => Some(1), i => Some(factorial::<{X - 1}>().unwrap() + i) } } fn main() { println!("{:?}", factorial::<10>()); }
しかし、そうではありません。
warning: the feature `const_generics` is incomplete and may cause the compiler to crash --> src/main.rs:1:12 | 1 | #![feature(const_generics)] | ^^^^^^^^^^^^^^ error: internal compiler error: src/librustc_codegen_ssa/mir/operand.rs:79: unevaluated constant in `OperandRef::from_const`
本当に符号なしの型を使用する必要がありますが:
#![feature(const_generics)] fn factorial<const X: u32>() -> u32 { match X { 0 => 1, 1 => 1, _ => factorial::<{ X - 1 }>() + X, } } fn main() { println!("{:?}", factorial::<10>()); }
この機能は私にも役立ちます。 const fn
では不十分です。 終了条件がdimensions = 0のN次元配列に使用したいと思います。
構造体を作成できます:
#![feature(const_generics)]
struct MyArray<T: Default, const len: usize> {
real_array: [T; len]
}
impl<T: Default, const len: usize> MyArray<T, {len}> {
fn new() -> Self {
return MyArray {
real_array: [Default::default(); len]
}
}
}
次の理由で実際にアレイを初期化できないため、これは失敗します。
error: array lengths can't depend on generic parameters
--> src/main.rs:9:46
|
9 | real_array: [Default::default(); len]
|
const値をgeneric値に設定することで回避しようとしました。
それは上記のエラーを乗り越えますが、コンパイラはクラッシュします。
error: internal compiler error: src/librustc/ty/subst.rs:597: const parameter `height/#0` (Const { ty: usize, val: Param(height/#0) }/0) out of range when substituting substs=[]
この言語は、標準ライブラリが配列のサイズごとに各関数を定義する必要がないように、基本的な定数ジェネリックを必死に必要としています。 これが私が錆を使わない唯一の理由です。 完全なコンパイル時関数をチューリングする必要が本当にありますか?
私は間違っているかもしれませんが、単純な整数式で十分であり、これらの風変わりな例が機能することを確認して、誰も時間を無駄にしないことを願っています。
この言語は、標準ライブラリが配列のサイズごとに各関数を定義する必要がないように、基本的な定数ジェネリックを必死に必要としています。
https://github.com/rust-lang/rust/issues/61415にはすでにいくつかの取り組みがあり
これらの奇抜な例が機能することを確認して、誰も時間を無駄にしていないことを願っています。
一部の人々はそうします、私はそれで何の問題も見ることができません;)
これが私が錆を使わない唯一の理由です。
これは、さびを使用していない人のために引用された私が見た中で最も興味深い理由ではありません。 本当のことを言っているのですか?
しかし、単純な整数式で十分です
その縮小されたスコープでさえ、信じられないほど難しいです。 それを試してみてください。私たちは本当に有能な人々がこれを作っています、そしてそれがこれほど長くかかる理由があります。
残念ながら、私は最近constジェネリックのバグを修正するために(またはより一般的にはRustに)多くの時間を割くことができませんでした。 誰かがconstジェネリックスの推進に参加したい場合は、未解決の問題に取り組み、バグ修正を確認するためのアドバイスを提供させていただきます。ただし、これに再び集中できるようになるまでには少し時間がかかるでしょう。
これらの奇抜な例が機能することを確認して、誰も時間を無駄にしていないことを願っています。
miri
はすでにC ++ constexpr
よりはるかに強力です。
そして、 N + 1 = M + 1
からN = M
を導出するような、凝ったことに取り組んでいる人は誰もいません。
これらのバグのほとんどは、それらの式に関するものではなく、型システムと、 const
ジェネリックが他のすべての機能とどのように相互作用するかに関するものです。
あなたが持っていたのがconst
ジェネリックと整数リテラルだけだったとしても、それらはまだそこにあります。
ところで、 [expr; N]
式の「配列の長さはジェネリックパラメーターに依存できない」エラーは必要ないと思います。 [T; N]
タイプの場合と同じトリックを使用して、プルすることができます。式として評価せずにN
を出力します。
私はこれを突き刺したいのですが、私が正しい人かどうかはわかりません。 私はさびをほとんど使用せず、コンパイラ理論をほとんど知りません。 かなりのコーチングが必要かもしれませんが、私は確かに喜んでいます。 😄
編集:私はソフトウェア全般についてかなりの経験があります。
@varkor 、私はrustc
で何か役に立つことを探していました、そして私はステップアップして手伝いたいです。 また、それに時間を割り当てるあなたの能力について率直であることに感謝します!
@varkor 、私は実際に上記のコメントをフォローしているので、何かアドバイスがあれば、私はすべての耳です! 他の通信チャネルもお気軽にご紹介ください。
私たちが今できることの1つは、 Foo::<{...}>
/ [expr; ...]
式がジェネリックパラメーター( ...
部分)を参照できるようにすることです。
これは、式を本体内のどこかにネストする必要があり、循環依存を防ぐ
ただし、署名に[(); [0; 1][0]]
れていると壊れてしまうのではないかと心配しているので、とにかくクレーターを実行する必要があり
constジェネリックを手伝うことに興味がある人のために、私のアドバイスは、開いているconstジェネリックの問題のリストを見て、あなたが興味を持って@eddyb、@yodaldevoid、@ OLI-OBKと私は、関連分野の多くに精通していると聞いて良い人です。 ご関心をお寄せいただきありがとうございます。
cc @ hameerabbasi 、 @ ranweiler
constジェネリックに関する質問:
const
パラメータを使用する場合、現時点ではどのような制限がありますか?const
ジェネリックのどの重要な機能が実装されていない/コンパイラをクラッシュさせますか?PS(寄稿者:)この機能に取り組んでいただきありがとうございます。
const-genericsはまだ開発中であるため、公式のドキュメントはまだありません。 他の2つの質問についてはよくわかりません。 const-genericsを最後に使用したとき、いくつかのconst-genericパラメーターを指定するとクラッシュしましたが、最後に何かを行ってから1か月近く経っているため、状況が変わった可能性があります。
そして、
N + 1 = M + 1
からN = M
を導出するような、凝ったことに取り組んでいる人は誰もいません。
これは、このような型制約のソルバーがあると非常に便利です。 たとえば、2つのNビット数の場合に加算を実装する場合、オーバーフロービットを考慮してリターンサイズを(N + 1)にする必要があります。 (たとえば)2つの5ビット数が与えられた場合、ソルバーはN + 1 = 6
をチェックする必要があります。 うまくいけば、これは後でconstジェネリックにボルトで固定することができます:)
@ flip111はい、後でこれを追加する予定だと思いますが、この種の一般的な式の制約は非常に複雑で、実装が困難です。 したがって、少なくとも数年間はそれらが表示されない可能性があります。
正直なところ、これらの種類の制約ベースの問題を解決することは、Prologの仕事のように聞こえます。 多分チョークエンジンに便乗することはオプションですか? Afaikそれは特性を解決します、しかし、そうですか?
ところで、私はこのatmを手伝う余裕はありませんが、このトピックは大好きです。 あなたの時間とコミットメントのためにConstGenericsに取り組んでいるすべての人に感謝します。 💐
小さな更新:私はRustツリーを上っていきます。 私は貢献するつもりですが、過度のコーチングを必要としない場所に自習したいと思っています。
構造体を作成できます:
#![feature(const_generics)] struct MyArray<T: Default, const len: usize> { real_array: [T; len] } impl<T: Default, const len: usize> MyArray<T, {len}> { fn new() -> Self { return MyArray { real_array: [Default::default(); len] } } }
次の理由で実際にアレイを初期化できないため、これは失敗します。
error: array lengths can't depend on generic parameters --> src/main.rs:9:46 | 9 | real_array: [Default::default(); len] |
同じ問題が発生しましたが、
MaybeUninit
を使用した回避策を見つけました:
https://play.rust-lang.org/?version=nightly&mode=release&edition=2018&gist=3100d5f7a4efd844954a6fa5e8b8c526
適切に初期化された配列を取得することは明らかに単なる回避策ですが、私にとっては、適切な方法が利用可能になるまでこれで十分です。
注:コードは常に意図したとおりに機能するはずですが、誰かが安全でないものを使用してバグを見つけた場合は、喜んで修正します。
@raidwas =
演算子を使用して初期化されていないメモリを初期化すると、初期化されていないメモリが削除されます。 代わりにこれを行ってください
@KrishnaSannasiありがとう、良いキャッチ:D(技術的には、プリミティブにのみ使用するため、初期化されていないメモリを削除しませんでしたが、ここに正しいバージョンがあるのは良いことです)
技術的には、フロートでさえドロップすることは定義されていませんが、現時点では悪用できません。
技術的には、フロートでさえドロップすることは定義されていませんが、現時点では悪用できません。
初期化されていないタイプであっても、 Copy
タイプを削除することは、何の操作もないと思っていたでしょう。 多分それは変える価値があります。
技術的には、フロートでさえドロップすることは定義されていませんが、現時点では悪用できません。
初期化されていないタイプであっても、
Copy
タイプを削除することは、何の操作もないと思っていたでしょう。 多分それは変える価値があります。
それはまだ技術的にはUBです。 定義されたCopy
値を安全に削除することはできませんが、コンパイラは、初期化されていない任意のタイプのメモリを削除しようとすると(たとえば、その値に触れる可能性のあるすべてのコードを削除しようとすると)、予期しない最適化を行うことを決定する場合があります。物事を壊す可能性があります。 それが私の理解です。
失礼ではありませんが、60人以上がこのスレッドへのコメントについて通知を受けるので、トピック外の議論は最小限に抑える必要があります。 これを使用して、constジェネリックの作業を調整しましょう。これは、私たち全員が望んでいることです。
別のクレート(依存関係)からconst-param_を持つタイプを指す_aliasesを使用するとクラッシュします。
例えば:
パブタイプエイリアス
pub struct Struct
- crate B
extern crate crate_a;
crate_a :: Aliasを使用します;
pub fn inner_fn(v:エイリアス
`` `
クラッシュログ(44580).txt
@ fzzr-#64730のように聞こえます
私はいくつかのコードをconstジェネリックに切り替えてきましたが、実際には2つの異なるユースケースがあるようです。 それらを混同する必要があるのか、それともユースケースに2つの異なる構文を使用するほうがよいのかわかりません。
私の使用法の多くは、実際には定数値が型を決定する役割を果たす型ではなく、非定数/リテラル値(まだ完全にはサポートされていない、たとえば一致パターン)に対してロックアウトされている機能を利用するためのものです。しかし、最終的にはそうする必要があります)。
私見では、constジェネリックと一緒に「const引数」を正式に配置する必要があります。これにより、コンパイラに特定の変数をリテラル/定数として評価させるために、1000個の「constジェネリック」(引数ごとに1つ)を導入するミュータントコードを記述しないようにします。
@mqudsi例を挙げて
つまり、再利用のために次のような一般的なパーサーコードをリファクタリングする場合です。
let mut result: u32 = 0;
let mut digits = 0;
while digits < max_digits && src.has_remaining() {
match src.get_u8() {
d<strong i="6">@b</strong>'0'..=b'9' => {
result = result*10 + (d - b'0') as u32;
digits += 1;
},
b'>' => match result {
0..=191 => break, // valid range
_ => return Err(DecoderError::OutOfRange),
},
_ => return Err(DecoderError::Malformed)
}
}
通常は関数に移動しますが、パターンマッチングで使用される特定の値が変数になっているため、以下は機能しません。
#[inline(always)]
fn read_str_digits<B: bytes::buf::Buf>(src: &mut B, stop_word: u8,
max_digits: u8, min_value: u32, max_value: u32) -> Result<u32, DecoderError> {
let mut result: u32 = 0;
let mut digits = 0;
while digits < max_digits && src.has_remaining() {
match src.get_u8() {
d<strong i="10">@b</strong>'0'..=b'9' => {
result = result*10 + (d - b'0') as u32;
digits += 1;
},
stop_word => match result {
min_value..=max_value => break, // valid range
_ => return Err(DecoderError::OutOfRange),
},
_ => return Err(DecoderError::Malformed)
}
}
...
}
constジェネリックがconst引数の前に着地した場合、私は突然constジェネリックを悪用して次のことを思い付く可能性があります。
#[inline(always)]
fn read_str_digits<const MinValue: u32, const MaxValue: u32, const StopWord: u8, B: bytes::buf::Buf>
(src: &mut B, max_digits: u8) -> Result<u32, DecoderError> {
let mut result: u32 = 0;
let mut digits = 0;
while digits < max_digits && src.has_remaining() {
match src.get_u8() {
d<strong i="14">@b</strong>'0'..=b'9' => {
result = result*10 + (d - b'0') as u32;
digits += 1;
},
StopWord => match result {
MinValue..=MaxValue => break, // valid range
_ => return Err(DecoderError::OutOfRange),
},
_ => return Err(DecoderError::Malformed)
}
}
...
}
今日の時点では、コンパイラがconstジェネリック値をパターンでの使用に有効な定数として検出しないため、このコードでも機能しませんが、これは明らかに正しくなく、RFC2000を実行する前に対処する必要があります。 誤解しないでください。私は何年もの間ジェネリック定数を求めて戦ってきました。PRは12個の主要なクレートに行く準備ができています( chrono
のタイムゾーンをジェネリック定数にするのが待ちきれません。さまざまなDateTime
タイプをすべて統合します)が、constジェネリックを悪用してconst引数を偽造することが可能になった場合(「const」とは、実際には「リテラル」を意味します)、広く見られるようになります。その乱用。
それは必ずしも世界の終わりではないのですが、私たちは同様に余分な時間がかかる場合がありますので、適切かつ完全なconstのジェネリックの実装は、必ずしも、とにかくconstの引数のすべての配管が含まれるかのようにあまりにも深く掘っがなくても、それは思えませんconst引数のsyntax / ux / storyを完成させ、コードの悪臭の不幸な時代を回避します。 はい、上記はマクロでも実行できますが、constジェネリックの人間工学は1000倍簡単です。
fwiw、これは私がconst引数バージョンがどのように見えるかを想像する方法です:
#[inline(always)]
fn read_str_digits<B: Bytes>(src: &mut B, min_value: const u32,
max_value: const u32, stop_word: const u8, max_digits: u8) -> Result<u32, ()> {
let mut result: u32 = 0;
let mut digits = 0;
while digits < max_digits && src.has_remaining() {
match src.get_u8() {
d<strong i="23">@b</strong>'0'..=b'9' => {
result = result*10 + (d - b'0') as u32;
digits += 1;
},
stop_word => match result {
min_value..=max_value => break, // valid range
_ => return Err(()),
},
_ => return Err(())
}
}
...
}
これは、constジェネリックと実質的に同じですが、セマンティクスが異なります。
通常は関数に移動しますが、パターンマッチングで使用される特定の値が変数になっているため、以下は機能しません。
これは、バインドされた変数の値を(constsだけでなく)パターンで使用できるようにすることで、いくつかの新しい構文を使用して、より簡単に対処できるユースケースのようです。 constsが現在パターンで許可されているという事実は直感に反し、私が知る限り、歴史的です。
@mqudsiこれがばかげた質問であるなら許してください、しかし私はあなたが与えた例について何も悪いことを見ません。 私には、constジェネリックの完全に有効なユースケースのように見えます。最大/最小として任意の値で機能するように一般化された定義を持っています。 constジェネリックよりもconst引数の利点はあまりわかりません。 彼らは私と同等のようです。 つまり、const引数は、constジェネリックスへの脱糖として実装できます。 このデザインパターンの何が問題になっているのか、詳しく説明していただけますか?
前回の更新以降
さらに、コンパイラに関する他の作業により、constジェネリックに関連する他の問題のいくつかが修正されました。
また、コンパイラ自体の内部でconstジェネリックの使用を開始しました。 @ crlf0710と@scottmcm (https://github.com/rust-lang/rust/pull/60466、https)の作業のおかげで、配列トレイトの実装でconstジェネリックが使用されるようになりパフォーマンスが向上し、将来的に(constジェネリックが安定化されたときに)制限解除できるようになります。 @Centrilは、同じアプローチを使用してVecDeque
を改善しました(https://github.com/rust-lang/rust/pull/63061)。
constジェネリックに関する特に一般的なバグの多くは、過去数か月で修正されています。 @nikomatsakisは怠惰な正規化を調査しており、残りの問題のホストを修正する必要があります。 @ jplatteは、型パラメーターからのconstパラメーターの解消の修正を検討しています(したがって、 {X}
と入力する必要はありません。 const引数)。
また、 @ eddybが、開発全体を通じてconstジェネリックを指導およびレビューしてくれたことに感謝します。これは非常に貴重です。
対処すべき問題は他にもたくさんあります。以前と同様に、私たちが得ることができるすべての支援を利用できます。残りの問題のいずれかに取り組むことに興味がある場合は、この
@mqudsi @ mark-imジェネリックの現在の構文上の代替案(引数の位置にimpl Trait
)をconstジェネリックに拡張することは適切でしょうか? これは、このユースケースで推奨される構文@mqudsiです。
個人的には、これによりそのようなユースケースの可読性が向上すると思いますが、1つの問題があります。通常のジェネリックがデータの受け渡しに使用されるため、引数にバインドされます。 これはconstジェネリックには当てはまらないので、どのようにそれらを渡しますか? それらが議論であるふりをしますか?
引数の位置がimpl Trait
の場合、turbofish構文でも使用できません。 引数のconst
場合、逆になります。 これは混乱を招く可能性があります。
ここでの「奇妙さ」をメリットが上回るかどうかはわかりませんが、とにかく自分の考えを共有したいと思いました。
私は以前にこれについての議論を漠然と思い出しました、そして今私はそれを見つけました: https :
@ PvdBerg1998それは非常に悪い考えのようです。 <…>
が読みにくい構文であるという考えは本当によくわかりません。 ここで2つの可能性:
where
節に置くことでしょう?ここで、上記のリンク先のRFC以前と比較してください。 これは、(型システムの意味での)_tag_を関数の引数の位置に直接導入することです。つまり、関数の本体の変数バインディングです。
言い換えれば、それは非常に紛らわしく、何人かの人々がargの位置にあるimpl Traitについて述べたように、それは必要ありません。 価値のない機能を追加するために、同じ間違いを2回繰り返さないでください。 特にあなたが「彼らが議論であるふりをする」ならば。 ここでは錆が間違った方向に進んでいるでしょう。 構文はより厳密である必要がありますが、それほど混乱することはありません。 停止してください。
@phaazon
私は個人的に、引数の位置に定数を許可することが人間工学的に非常に優れた後押しになるというリンクされた提案に完全に同意します。 これは、関数のシグネチャ(おそらく、 where
句は問題を「解決」しないだけでなく、1つではなく3つの場所を解析する必要があるため、シグネチャを理解するのが難しくなります)だけでなく、これらの関数がどのように使用されるか。 比較:
let r = _mm_blend_ps::<{2 * C}>(a, b);
let r = _mm_blend_ps(a, b, 2 * C);
私の意見では、2行目は2行目よりもはるかに自然です。 別の例は、マトリックスの作成です: MatrixF32::new(3, 3)
対MatrixF32::new::<3, 3>()
。 この機能が「非常に紛らわしい」ものになることに強く反対します。 ユーザーにとっては、関数の引数の追加要件にすぎません。 マクロを介してconst引数をエミュレートすることはすでにできますが( std
内のSIMD組み込み関数を参照)、非常に醜く非効率的です。
議論の立場にあるimpl Trait
に関してimpl Trait
引数を非表示にすることだと思います。つまり、ユーザーは、明示的な型の曖昧性解消が必要ないことが確実な場合にそれを使用する必要があります。 これにより、シナリオによっては、ターボフィッシュ内の_
必要性を大幅に減らすことができます。
const引数の議論はここではトピックから外れていると思うので、おそらくここでそれを続けるべきではありません。
const
引数をimpl Trait
引数( @ PvdBerg1998で言及)からが引数であり、型推論を意味するすべてのことです。
型自体を引数として渡すことはできませんが、値を渡すこと
これは、C ++ではかなり一般的です。次に例を示します。
template <typename T, size_t N>
size_t length(T (&array)[N]) {
return N;
}
これは、単にconstパラメーターを受け入れてそれをconstジェネリックパラメーターに脱糖することとfn foo(const N: usize)
-> fn foo<const N: usize>()
)。 これは、constジェネリックパラメーターを使用してパラメーターを制約し、引数からそのconstジェネリック引数を推測することに近いものです。 したがって、上記のマトリックスの例は次のようになります。
impl<const W: usize, const H: usize> MatrixF32<W, H> {
fn new(w: usize<W>, h: usize<H>) -> Self { .. }
}
...ここで、 usize<X>
は、「値がX
usize
」の構文で構成されています。 (同様に、 NonZero
の一般化のコンテキストで説明されている、範囲タイプにはusize<X..Y>
が必要になる場合があります。)
範囲型でのローピングを回避したい場合は、スコープを少し調整して、パラメーターを型で直接使用できるようにする依存型言語を検討してください。
fn new(const W: usize, const H: usize) -> MatrixF32<W, H> { .. }
それらは議論ではありません。 それらはジェネリックです。 型システムレベルでは、これは、それらの_values_がコンパイル時に存在し、関数の引数の値が実行時に存在することを意味します。 両方を混合することは非常に誤解を招く恐れがあります。
私が型システムに関心を持っているので、この提案は多くの正気で健全な原則に反します。 値とタイプを混在させたくありません。 はい、 N
( usize
とは言わなかったので、 N
と言いました)は、値だと思っていても、タイプに非常に似ているためDataKinds
、通常のデータコンストラクターを型として持ち上げることができます。
foo :: Integer -> Integer
foo 0 -- 0 has type Integer
-- but
data P (a :: Integer)
type MyP = P 10 -- 10 has kind Integer, which “value” is the 10 type
そう:
fn foo<const X: usize>()
ここで、 X
は、値よりも型に似ています。
また、単形化も気になります。 私がこれを読んだ場合:
let x = foo(12, "Hello, world!", None);
あなたの提案で、 foo
の定義を調べると、どの引数がfoo
を独占するのかを知るのは困難です。 constジェネリックに異なる値を渡すたびに、完全に新しい関数を作成します。
_you_と_yourreasons_の方が直感的だと感じるかもしれませんが、タイプの正確さに関してはまったく直感的ではないと言う理由もあります。 それらが引数であると述べることは、数学では、パラメーター化された関数がそれらのパラメーター引数を持っていると述べることに似ています。 あなたはパラメータと引数を混乱させており、そのアイデアがいかに悪いかを示しています。
あなたは私を誤解しているかもしれません、私は特に「 const
ジェネリック」ではなく「 const
引数...は引数です」と言いました。 私の最初のRustの例も、DataKindsとまったく同じです( usize<N>
はN
のタイプレベルバージョンです)。 ここでは型の正確さに関しては問題はありません。私は直感の観点から議論しているのではなく、既存の確立された健全な型システムとの類似性によって議論しています。
単形化についてのあなたの懸念については-それは今日と同じです! すべての議論は潜在的に新しい単形化を引き起こす可能性があります。 また、この問題の解決策は、インスタンス間で非依存コードを共有することによって、すべてのジェネリックのコストを削減することです。これには、特にconstジェネリックを可能な場合はランタイム値に変換することも含まれます。
(そして、私はパラメーターと引数を混同しているというあなたの主張に完全に不思議に思っています、私はそれらを区別するように注意しました。)
私はあなたが言っていることを理解しますが、それでも私は非常に心配しています。 _argument_は_const_のタグが付けられているため、実行時の話では_avalue_を渡すことができないことを意味します。 _constgeneric_にマップされる_constantvalue_を渡す必要があります。 ちなみに、ここでは言及していませんが、 10
をタイプと見なすと、これは_シングルトンタイプ_のアプリケーションにすぎません。可能な単一の値は10
です。
私はargの位置でimplTraitに反対してきたので、私もそれに反対しています(そして、ここで比較できる理由がわかります)。 いくつかのポイント:
struct
とfn
考えると、 <…>
が(…)
より難しいと考える人がいる理由を本当に知りたいです。 現在、 struct
は<…>
しかなく、 fn
は両方があります。これは、両方にパラメーターを設定できるためです。foo<A, B>
を読むと、関数の定義と実装につながる2つのパラメーターがあることがわかります。ここでやりたいことを感じます。 foo(1, 3)
は、 foo<3>(1)
よりも気分が良くなりますが、私にとってはそうではありません。 これは、 foo(1, 3)
が2番目の引数をランタイムベースとして受け入れる必要があり、提供する提案がそれを禁止しているためです。 私は、特に配列を使用してアプリケーションを見ることができますが、現在、その外観が気に入らないのです。 「引数がconst
場合、型変数/ constgenericを推測できます。
fn foo<const N: usize>(a: usize, b: usize | N);
これは、私の主張を説明するために思いついた架空の構文です。 b
をconst値として渡すと、 N = b
なり、最終的には素晴らしい構文になります。
foo(1, 3)
b
がconst値でない場合は、 N
明示的に渡す必要があります。
foo<3>(1, x)
また、あなたの提案では、これの構文はどうなりますか?
fn foo<const N: usize>(x: [f32; N]);
fn new(const W: usize, const H: usize) -> MatrixF32<W, H> { .. }
つまり、
new()
は関連付けられた関数であり、具象型に関連付ける必要があります。constジェネリック項目は、「このパラメーターを持つこの型...」のようなセマンティクス、つまり依存型を持ちます。 したがって、コンテキスト内のnew()
には引数を含めるべきではありません。 明示的な使用法はType<32>::new()
ます。 そしてもちろん、すべてのconstジェネリック引数は省略可能である必要があります。
ここでやりたいことを感じます。 foo(1、3)は、foo <3>(1)よりも気分が良くなりますが、私にとってはそうではありません。 この理由は、foo(1、3)が2番目の引数をランタイムベースであると受け入れる必要があり、あなたが与える提案がそれを禁止しているためです。
それは私の動機でも意図でもありません。「気分が良くなる」ことについて話しているのではなく、コンパイル/実行時の区別をタイプ/値レベルの区別と混同する必要があるとは思いません。 2つはすでに別々です- const
アイテム(valueまたはfn)は確かにタイプではありません!
私が得ているのは、型推論が値レベルのパラメーターと型レベルのパラメーターの間に描くセマンティック接続だけです。 impl Trait
とは何の関係もありません。繰り返しになりますが、私のポイントは、const引数をimpl Trait
と区別することでした。これは、この種の非生産的な不安を回避するためです。
また、あなたの提案では、これの構文はどうなりますか?
fn foo<const N: usize>(x: [f32; N]);
その構文は変わりません! constパラメーターをタイプレベルから(構文的または意味的に)削除することを提案しているのではなく、タイプレベルのパラメーターの推論を部分的に支援するために、それらを値レベルに追加するだけです。
cppconで、constexpr関数パラメーターについての話がありました。これは、 @ PvdBerg1998が話しているものと似ており、@ mark-imがリンクしている内部スレッドと同じように見えます。 好評のようです。
良いアイデアのように思えますが、const-genericsの最初の実装が完了した後で解決する必要があります
CppCon 2019:David Stone-C ++からのメタプログラミングの削除、Nのパート1:constexpr関数パラメーター
編集:これはそのスレッドで言及されました、これはconstexpr関数パラメーターの関連論文です
https://github.com/davidstone/isocpp/blob/master/constexpr-parameters.md
内部のスレッドまたはRFCリポジトリの問題(またはPRの修正)のいずれかについて、このような重要な構文変更の議論を保留することを要求したいと思います。 この問題のコメントセクションはすでにかなり長く、問題の構文はconstジェネリックの使用可能なMVPには不要のようです。
コメントを移動できれば、おそらく移動します。 問題を@ rust-langチームメンバーにロックし、すべてのオフトピックコメントを非表示にすることをお勧めしますか?
すべての設計の議論はRFCリポジトリで行われるべきであり、すべてのバグレポートは別々の問題にあるべきです。
cc @ rust-lang / moderationこれを行う前例はありますか?
コメントを問題に_変換_することはできますが、コメントを移動することはできません。 ロックは問題ありません。トピックのコメントを非表示にします。
新年の始まりとともに、私たちは現在constジェネリックを使用している場所について別の更新を行うのが良いと思いました。 constジェネリックRFCが受け入れられてから2年余りが経ちました。 最初の年(およそ2018年)に、コンパイラ全体でジェネリックパラメータの処理を改善することにより、constジェネリックを容易にするために、一連の大規模なリファクタリングの取り組みが行われました。 2年目(およそ2019年)に、constジェネリック自体の実装に取り組み始めました。最初に最小限の動作を実現し、次に機能の整合性の向上を遅らせることでした。 人々はまた、コンパイラ自体でconstジェネリックを使用する実験を始めました。 これらの取り組みのより詳細な説明は、最初の2つの更新投稿[1] 、 [2]にあります。
前回の更新以降、過去2か月で順調に進展しています。
{}
でラップする必要がなくなりました(https://github.com/rust-lang/rust/pull/66104)structural_match
チェックを実装して、カスタム等価性チェックを使用する型がconstジェネリックとして使用されることを禁止しました(https://github.com/rust-lang/rust/pull/65627)constジェネリックを手伝ってくれたすべての人に感謝します!
次は何ですか? 現時点でのconstジェネリックの最大のブロッカーは、レイジー正規化です。これは、特定の種類のconstジェネリック境界(配列など)に必要です。 @ skinny121は現在、怠惰な正規化を取り除くための素晴らしい努力を続けています。 これにより、constジェネリックに関する現在の問題の多くに対処できます。
怠惰な正規化はさておき、私たちが対処したいバグやペーパーカットはまだかなりの数あります。 残りの問題のいずれかに取り組むことに興味がある場合は、問題について私にpingするか、DiscordまたはZulipで開始の指針を確認してください。 2020年には順調に前進できると確信しており、安定化が実行可能な議論になるポイントに近づくことを願っています。
怠惰な正規化にヒットせず、バグやペーパーカットがあまりないconstジェネリックのサブセットはありますが、有用なコードを大量に表現できますか? 配列の多くの特性のstdの実装は問題なく機能しているように見えることに気付きました。 おそらく、他のクレートが、すべてのより洗練された機能をサポートしていなくても、独自の特性のためにstdにある種類のimplを記述できるようにするナローイングがありますか?
今年は半ば以上になりましたので、これまでの作業を簡単にまとめます。 過去6か月間、constジェネリックスのサポートの改善に携わった多くの人々がいましたので、何らかの形で助けてくれたすべての人に感謝します! 特に@ skinny121と@lcnrに感謝します。どちらも、constジェネリックがしばらく欠けていたいくつかの大きな機能に取り組んできました。定数の遅延正規化とメソッド呼び出しでの
Constジェネリックは現在、コンパイラ全体のいくつかの場所で使用されており、constジェネリックを使用する特性と関数の実験がすでに行われています(例: slice::array_chunks
とIntoIterator
とFromIterator
for [T; N]
(https://github.com/rust-lang/rust/pull/65819、https://github.com/rust-lang/rust/pull/69985)。 配列特性の実装に関する長さ32の制限も、最終的に削除の準備が
現在、幅広いユースケースをカバーする必要があるconstジェネリックのサブセットの安定化について議論しています(ただし、constジェネリックを完全に安定化する前に対処する必要のある問題がまだいくつかあります)。
unused_braces
lintを追加しました(https://github.com/rust-lang/rust/pull/70081)dyn Trait
を禁止します(https://github.com/rust-lang/rust/pull/71038)constジェネリックはすでに長い道のりを歩んできましたが、まだバグと鋭いエッジがあります。 残りの問題のいずれかに取り組むことに興味がある場合は、この問題について@lcnrまたは@eddybに、または遠慮なくpingしてください。 ありがとうございました!
最も参考になるコメント
これは、constジェネリックのこれまでの進捗状況の要約です。
constジェネリックの作業を適切に開始する前に、実行する必要のあるリファクタリングがいくつかありました。 @jplatteはhttps://github.com/rust-lang/rust/pull/45930でタスクを引き受けました@ jplatteはconstジェネリックのメイン実装の作業を
@yodaldevoidと私は、 @ jplatteがhttps : //github.com/rust-lang/rust/pull / 48452 、 https://github.com/rust-lang/rust/pull/48523、https: //github.com/rust-lang/rust/pull/51880。
これが完了すると、constジェネリックの実装が本格的に開始される可能性があります。 それ以来、 @ yodaldevoidと私はゆっくりと、しかし確実にconstジェネリックのサポートを段階的に追加してきました: https : //github.com/rust-lang/rust / pull / 58503 、 https : //github.com/rust-lang/rust/pull/58581、https://github.com/rust-lang/rust/pull/58583、https://github.com/rust -lang /錆/プル/ 59170 、 https://github.com/rust-lang/rust/pull/59355 、 https://github.com/rust-lang/rust/pull/59415 、 https://でgithubの.com / rust-lang / rust / pull / 60058 、 https ://github.com/rust-lang/rust/pull/60280、https://github.com/rust-lang/rust/pull/60284およびほとんど最近https://github.com/rust-lang/rust/pull/59008。 (これらは主に、メインのconstジェネリックプルリクエストから分割され
今の状況は? 一部のconstジェネリックテストが機能するようになりました🎉ただし、機能しないテストもあり、コード全体でまだ対処する必要のある
FIXME
がいくつかあります。 しかし、私たちは今近づいています、そしてあなたが助けたいのであれば、私たちはいくつかの手に負えない果物がある段階にあります。FIXME(const_generics)
散在しています。 私たちはそれらを処理することを計画していますが、 @ yodaldevoidと私には時間があまりないので、1つに取り組むことができると思う場合は、先に進んでください(コメントを残したいので、私たちはしません) t重複した努力)。constジェネリックが適切なテストの準備が整う前に、残りの実装の問題のいくつかの概要をトップの投稿に書き、何をすべきかについての大まかなアイデアを提供しました。
時間はかかりますが、着実に進んでおり、今後もペースを上げていきたいと考えています。 物事がうまくいき始めているのをようやく見るのはやる気を起こさせます!