هذه مشكلة تتبع للتخصص (rust-lang / rfcs # 1210).
خطوات التنفيذ الرئيسية:
default impl
(https://github.com/rust-lang/rust/issues/37653)default
؟ https://github.com/rust-lang/rust/issues/48444أسئلة لم يتم حلها من RFC:
default type
؟ أبدا أثناء الكتابة؟ أو عندما أحادي الشكل؟default
(أي قابلة للتخصيص)؟default impl
(حيث تكون جميع العناصر default
) أو partial impl
(حيث يكون default
مشتركًا) ؛ راجع https://github.com/rust-lang/rust/issues/37653#issuecomment -616116577 للحصول على بعض الأمثلة ذات الصلة حيث يتم تقييد default impl
.لاحظ أن ميزة specialization
كما تم تطبيقها حاليًا غير سليمة ، مما يعني أنها يمكن أن تسبب سلوكًا غير محدد بدون رمز unsafe
. min_specialization
يتجنب معظم المزالق .
بعض الأسئلة المفتوحة الإضافية:
لست متأكدا من أن التخصص يغير قواعد اليتيم:
والأسوأ من ذلك ، أن قواعد "التوافق المستقبلي" التي تبقي التخصص عبر الصناديق تحت سيطرة شديدة جدًا. بدونها ، يصبح ترك الضمانات الافتراضية أساليبها مفتوحة أسوأ بكثير.
لم أحب أبدًا التفكير السلبي الصريح. أعتقد أن تخصص التفكير السلبي الكلي هو حل وسط جيد.
هل ينبغي السماح بذلك مع التخصص كما هو مطبق؟ أم هل فاتني شيء؟
http://is.gd/3Ul0pe
نفس الشيء مع هذا ، كان من المتوقع أن يتم تجميعه: http://is.gd/RyFIEl
يبدو أن هناك بعض المراوغات في تحديد التداخل عند تضمين الأنواع المرتبطة. هذا بتجميع: http://is.gd/JBPzIX ، في حين أن هذا الرمز متطابقة بشكل فعال لا: http://is.gd/0ksLPX
هذا جزء من الكود الذي كنت أتوقع تجميعه مع التخصص:
#![feature(specialization)]
use std::str::FromStr;
struct Error;
trait Simple<'a> {
fn do_something(s: &'a str) -> Result<Self, Error>;
}
impl<'a> Simple<'a> for &'a str {
fn do_something(s: &'a str) -> Result<Self, Error> {
Ok(s)
}
}
impl<'a, T: FromStr> Simple<'a> for T {
fn do_something(s: &'a str) -> Result<Self, Error> {
T::from_str(s).map_err(|_| Error)
}
}
fn main() {
// Do nothing. Just type check.
}
فشل التجميع مع المترجم الذي يشير إلى تعارضات في التنفيذ. لاحظ أن &str
لا يطبق FromStr
، لذلك يجب ألا يكون هناك تعارض.
sgrif
كان لدي الوقت للنظر في أول مثالين. ها هي ملاحظاتي.
الحالة الأولى ، لديك:
FromSqlRow<ST, DB> for T where T: FromSql<ST, DB>
FromSqlRow<(ST, SU), DB> for (T, U) where T: FromSqlRow<ST, DB>, U: FromSqlRow<SU, DB>,
تكمن المشكلة في أن هذه الضمانات تتداخل ولكن ليس أي منها أكثر تحديدًا من الآخر:
T: FromSql<ST, DB>
حيث لا يكون T
زوجًا (لذا فهو يتطابق مع الضم الأول وليس الثاني).(T, U)
حيث:T: FromSqlRow<ST, DB>
،U: FromSqlRow<SU, DB>
، لكن _not_(T, U): FromSql<(ST, SU), DB>
(T, U)
مثل:T: FromSqlRow<ST, DB>
U: FromSqlRow<SU, DB>
(T, U): FromSql<(ST, SU), DB>
هذا هو نوع الموقف الذي قد تسمح به الشبكة الشبكية - عليك كتابة ضمانة ثالثة للحالة المتداخلة ، وشرح ما يجب أن تفعله. بدلاً من ذلك ، قد تمنحك السمات السلبية الضمنية طريقة لاستبعاد التداخل أو تعديل المطابقات الممكنة.
عندك:
Queryable<ST, DB> for T where T: FromSqlRow<ST, DB>
Queryable<Nullable<ST>, DB> for Option<T> where T: Queryable<ST, DB>
تتداخل هذه العوامل لأنه يمكن أن يكون لديك Option<T>
حيث:
T: Queryable<ST, DB>
Option<T>: FromSqlRow<Nullable<ST>, DB>
لكن لا يوجد ضمني أكثر تحديدًا:
T
بحيث أن T: FromSqlRow<ST, DB>
لكن T
ليس Option<U>
(يتطابق مع الضم الأول وليس الثاني)Option<T>
مثل T: Queryable<ST, DB>
لكن ليس Option<T>: FromSqlRow<Nullable<ST>, DB>
تضمين التغريدة
فشل التجميع مع المترجم الذي يشير إلى تعارضات في التنفيذ. لاحظ أن
&str
لا يطبقFromStr
، لذلك يجب ألا يكون هناك تعارض.
المشكلة هي أن المترجم يفترض بشكل متحفظ أن &str
قد يأتي لتنفيذ FromStr
في المستقبل. قد يبدو هذا سخيفًا في هذا المثال ، ولكن بشكل عام ، نضيف إشارات ضمنية جديدة طوال الوقت ، ونريد حماية رمز المصب من الانهيار عند إضافة تلك الضمانات.
هذا اختيار متحفظ ، وقد نرغب في الاسترخاء بمرور الوقت. يمكنك الحصول على الخلفية هنا:
شكرا لتوضيح هاتين الحالتين. من المنطقي تماما الآن
في الثلاثاء ، 22 مارس 2016 ، 6:34 مساءً كتب آرون تورون [email protected] :
SergioBenitez https://github.com/SergioBenitez
فشل التجميع مع المترجم الذي يشير إلى تعارضات في التنفيذ. ملحوظة
هذا & str لا يطبق FromStr ، لذلك لا ينبغي أن يكون هناك تعارض.المشكلة هي أن المترجم يفترض بحذر أن & str
قد يأتي لتنفيذ FromStr في المستقبل. قد يبدو ذلك سخيفًا
هذا المثال ، ولكن بشكل عام ، نضيف إشارات ضمنية جديدة طوال الوقت ، ونريد ذلك
حماية رمز المصب من الانهيار عندما نضيف تلك الضمانات.هذا اختيار متحفظ ، وقد نرغب في الاسترخاء
متأخر، بعد فوات الوقت. يمكنك الحصول على الخلفية هنا:-
http://smallcultfollowing.com/babysteps/blog/2015/01/14/little-orphan-impls/
- الصدأ لانج / rfcs # 1023 https://github.com/rust-lang/rfcs/pull/1023
- الصدأ لانج / rfcs # 1053 https://github.com/rust-lang/rfcs/issues/1053
- الصدأ لانج / rfcs # 1148 https://github.com/rust-lang/rfcs/pull/1148
-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرةً أو قم بعرضه على GitHub
https://github.com/rust-lang/rust/issues/31844#issuecomment -200093757
تضمين التغريدة
تكمن المشكلة في أن المترجم يفترض بحذر أن & str قد يأتي لتنفيذ FromStr في المستقبل. قد يبدو هذا سخيفًا في هذا المثال ، ولكن بشكل عام ، نضيف إشارات ضمنية جديدة طوال الوقت ، ونريد حماية رمز المصب من الانهيار عند إضافة تلك الضمانات.
أليس هذا بالضبط ما يحاول التخصص معالجته؟ مع التخصص ، أتوقع أنه حتى إذا تمت إضافة تطبيق FromStr
لـ &str
في المستقبل ، فإن التنفيذ المباشر للسمة Simple
لـ &str
الأسبقية.
SergioBenitez تحتاج إلى وضع default fn
في التضمين الأكثر عمومية. الخاص بك
المثال غير متخصص.
في الثلاثاء ، 22 مارس 2016 ، الساعة 6:54 مساءً ، سيرجيو بينيتيز ، إخطارات github.com
كتب:
aturon https://github.com/aturon
المشكلة هي أن المترجم يفترض بحذر أن & str
قد يأتي لتنفيذ FromStr في المستقبل. قد يبدو هذا سخيفًا لهذا
على سبيل المثال ، ولكن بشكل عام ، نضيف إشارات ضمنية جديدة طوال الوقت ، ونريد ذلك
حماية رمز المصب من الانهيار عندما نضيف تلك الضمانات.أليس هذا بالضبط ما يحاول التخصص معالجته؟ مع
التخصص ، أتوقع أنه حتى لو تم تنفيذ FromStr
تم إضافة for & str في المستقبل ، التنفيذ المباشر لـ
ستأخذ سمة & str الأسبقية.-
أنت تتلقى هذا لأنه تم ذكرك.
قم بالرد على هذا البريد الإلكتروني مباشرةً أو قم بعرضه على GitHub
https://github.com/rust-lang/rust/issues/31844#issuecomment -200097995
أعتقد أن عناصر السمات "الافتراضية" التي يتم اعتبارها تلقائيًا default
تبدو مربكة. قد ترغب في كلٍّ من المعلمتين لسمة كما في Haskell ، وما إلى ذلك جنبًا إلى جنب مع تخفيف impl
s. كما لا يمكنك بسهولة grep
بالنسبة لهم كما يمكنك مقابل default
. ليس من الصعب كتابة الكلمة الرئيسية default
وتقديم تطبيق افتراضي ، ولكن لا يمكن فصلهما كما هو. أيضًا ، إذا أراد المرء توضيح اللغة ، فيمكن إعادة تسمية عناصر السمات "الافتراضية" هذه إلى عناصر "سمة مقترحة" في التوثيق.
ملاحظة من رقم 32999 (تعليق) : إذا
تضمين التغريدة
لماذا لا تعمل؟ الحيلة تحدد التخصص بسمة خاصة. لا يمكنك تخصيص السمة الخاصة إذا لم تتمكن من الوصول إليها.
@ arielb1 آه. نقطة جيدة. في حالتي ، السمة ليست خاصة.
لا أعتقد أن الاستدلال "الخارجي لا يمكن أن يتخصص لأن التوافق مع المستقبل + قواعد الاتساق اليتيمة" مثير للاهتمام أو مفيد بشكل خاص. خاصة عندما لا نلتزم بقواعد الاتساق المحددة الخاصة بنا.
هل هناك طريقة للوصول إلى default impl
تجاوزه؟ إذا كان الأمر كذلك ، فقد يساعد ذلك في إنشاء الاختبارات. انظر التصميم حسب العقد و libhoare .
سيسمح السماح بإسقاط الأنواع الافتراضية المرتبطة أثناء فحص النوع بفرض عدم المساواة في النوع في وقت الترجمة: https://gist.github.com/7c081574958d22f89d434a97b626b1e4
#![feature(specialization)]
pub trait NotSame {}
pub struct True;
pub struct False;
pub trait Sameness {
type Same;
}
mod internal {
pub trait PrivSameness {
type Same;
}
}
use internal::PrivSameness;
impl<A, B> Sameness for (A, B) {
type Same = <Self as PrivSameness>::Same;
}
impl<A, B> PrivSameness for (A, B) {
default type Same = False;
}
impl<A> PrivSameness for (A, A) {
type Same = True;
}
impl<A, B> NotSame for (A, B) where (A, B): Sameness<Same=False> {}
fn not_same<A, B>() where (A, B): NotSame {}
fn main() {
// would compile
not_same::<i32, f32>();
// would not compile
// not_same::<i32, i32>();
}
تم تحريره حسب تعليق burdges
فقط fyirphmeier يجب على المرء تجنب is.gd لأنه لم يتم حله لمستخدمي Tor بسبب استخدام CloudFlare. يعمل GitHub بشكل جيد مع عناوين URL الكاملة. ويعمل play.rust-lang.org بشكل جيد على Tor.
burdges FWIW يستخدم play.rust-lang.org نفسه is.gd للزر "Shorten" الخاص به.
ربما يمكن تغييره ، على الرغم من: https://github.com/rust-lang/rust-playpen/blob/9777ef59b/static/web.js#L333
استخدم مثل هذا (https://is.gd/Ux6FNs):
#![feature(specialization)]
pub trait Foo {}
pub trait Bar: Foo {}
pub trait Baz: Foo {}
pub trait Trait {
type Item;
}
struct Staff<T> { }
impl<T: Foo> Trait for Staff<T> {
default type Item = i32;
}
impl<T: Foo + Bar> Trait for Staff<T> {
type Item = i64;
}
impl<T: Foo + Baz> Trait for Staff<T> {
type Item = f64;
}
fn main() {
let _ = Staff { };
}
خطأ:
error: conflicting implementations of trait `Trait` for type `Staff<_>`: [--explain E0119]
--> <anon>:20:1
20 |> impl<T: Foo + Baz> Trait for Staff<T> {
|> ^
note: conflicting implementation is here:
--> <anon>:16:1
16 |> impl<T: Foo + Bar> Trait for Staff<T> {
|> ^
error: aborting due to previous error
هل يدعم feture specialization
هذا ، وهل هناك أي نوع آخر من التطبيقات حاليًا؟
تضمين التغريدة
لا يسمح تصميم التخصص الحالي بهذه التلميحات ، لأنه لا T: Foo + Bar
ولا T: Foo + Baz
أكثر تخصصًا من الآخر. بمعنى ، إذا كان لديك بعض T: Foo + Bar + Baz
، فليس من الواضح ما الذي يجب أن "يفوز".
لدينا بعض الأفكار حول نظام أكثر تعبيراً من شأنه أن يتيح لك _أيضاً_ تقديم ضمانة T: Foo + Bar + Baz
وبالتالي إزالة الغموض ، لكن لم يتم اقتراح ذلك بالكامل بعد.
إذا كانت السمة السالبة trait Baz: !Bar
الإطلاق ، فيمكن استخدام ذلك أيضًا مع التخصص لإثبات أن مجموعات الأنواع التي تنفذ Bar وتلك التي تطبق Baz متميزة وقابلة للتخصص بشكل فردي.
يبدو أن ردrphmeier هو ما أريده تمامًا ، كما أن الإشارة إلى T: Foo + Bar + Baz
ستساعد أيضًا.
فقط تجاهل هذا ، لا يزال لديّ شيء أفعله مع حالتي ، ودائمًا ما يكون مثيرًا للهبوط specialization
والميزات الأخرى.
بفضلaturonrphmeier.
لقد كنت ألعب مع التخصص مؤخرًا ، وقد صادفت هذه الحالة الغريبة:
#![feature(specialization)]
trait Marker {
type Mark;
}
trait Foo { fn foo(&self); }
struct Fizz;
impl Marker for Fizz {
type Mark = ();
}
impl Foo for Fizz {
fn foo(&self) { println!("Fizz!"); }
}
impl<T> Foo for T
where T: Marker, T::Mark: Foo
{
default fn foo(&self) { println!("Has Foo marker!"); }
}
struct Buzz;
impl Marker for Buzz {
type Mark = Fizz;
}
fn main() {
Fizz.foo();
Buzz.foo();
}
إخراج المترجم:
error: conflicting implementations of trait `Foo` for type `Fizz`: [--explain E0119]
--> <anon>:19:1
19 |> impl<T> Foo for T
|> ^
note: conflicting implementation is here:
--> <anon>:15:1
15 |> impl Foo for Fizz {
|> ^
أعتقد أن ما ورد أعلاه _يجب_ تجميعه ، وهناك نوعان مختلفان مثيران للاهتمام يعملان بالفعل على النحو المنشود:
1) إزالة الحد where T::Mark: Fizz
المربوط:
impl<T> Foo for T
where T: Marker //, T::Mark: Fizz
{
// ...
}
2) إضافة "اسم مستعار مرتبط بالسمة":
trait FooMarker { }
impl<T> FooMarker for T where T: Marker, T::Mark: Foo { }
impl<T> Foo for T where T: FooMarker {
// ...
}
(الذي _ لا يعمل_ إذا تم تعريف Marker
في صندوق منفصل (!) ، انظر هذا المثال الريبو )
أعتقد أيضًا أن هذه المشكلة قد تكون مرتبطة بالرقم 20400 بطريقة ما
تحرير : لقد فتحت مشكلة حول هذا: # 36587
أواجه مشكلة في التخصص. لست متأكدًا مما إذا كانت مشكلة في التنفيذ أو مشكلة في طريقة تحديد التخصص.
use std::vec::IntoIter as VecIntoIter;
pub trait ClonableIterator: Iterator {
type ClonableIter;
fn clonable(self) -> Self::ClonableIter;
}
impl<T> ClonableIterator for T where T: Iterator {
default type ClonableIter = VecIntoIter<T::Item>;
default fn clonable(self) -> VecIntoIter<T::Item> {
self.collect::<Vec<_>>().into_iter()
}
}
impl<T> ClonableIterator for T where T: Iterator + Clone {
type ClonableIter = T;
#[inline]
fn clonable(self) -> T {
self
}
}
( روضة )
(بالمناسبة ، سيكون من الجيد إذا وصل هذا الرمز في النهاية إلى stdlib يومًا ما)
فشل هذا الرمز مع:
error: method `clonable` has an incompatible type for trait:
expected associated type,
found struct `std::vec::IntoIter` [--explain E0053]
--> <anon>:14:5
|>
14 |> default fn clonable(self) -> VecIntoIter<T::Item> {
|> ^
يؤدي تغيير قيمة الإرجاع إلى Self::ClonableIter
ظهور الخطأ التالي:
error: mismatched types [--explain E0308]
--> <anon>:15:9
|>
15 |> self.collect::<Vec<_>>().into_iter()
|> ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected associated type, found struct `std::vec::IntoIter`
note: expected type `<T as ClonableIterator>::ClonableIter`
note: found type `std::vec::IntoIter<<T as std::iter::Iterator>::Item>`
على ما يبدو ، لا يمكنك الإشارة إلى النوع الملموس من النوع المرتبط المتخلف ، والذي أجده مقيدًا تمامًا.
tomaka يجب أن يعمل ، نص RFC يحتوي على هذا:
impl<T> Example for T {
default type Output = Box<T>;
default fn generate(self) -> Box<T> { Box::new(self) }
}
impl Example for bool {
type Output = bool;
fn generate(self) -> bool { self }
}
(https://github.com/rust-lang/rfcs/blob/master/text/1210-impl-specialization.md#the-default-keyword)
الذي يبدو مشابهًا بدرجة كافية لقضيتك ليكون ذا صلة.
satch هذا المثال لا يبدو أنه يجمع مع التعريف البديهي https://play.rust-lang.org/؟gist=97ff3c2f7f3e50bd3aef000dbfa2ca4e&version=nightly&backtrace=0
لا يسمح رمز التخصص صراحة بهذا - راجع # 33481 ، الذي اعتقدت في البداية أنه خطأ ولكن تبين أنه مشكلة تشخيصية. لقد مرت تقارير العلاقات العامة الخاصة بي لتحسين التشخيصات هنا دون أن يلاحظها أحد ، ولم أحافظ عليها لأحدث معلم منذ بعض الوقت.
rphmeier يقترح نص RFC أنه يجب السماح به على الرغم من نسخ هذا المثال منه.
لقد لعبت ببعض الأكواد التي يمكن أن تستفيد من التخصص. أعتقد بشدة أننا يجب أن نذهب إلى قاعدة الشبكة بدلاً من التسلسل - فهي تبدو طبيعية وكانت الطريقة الوحيدة للحصول على المرونة التي أحتاجها (AFAICT).
إذا اخترنا default
على impl
بالإضافة إلى العناصر الفردية ، فهل يمكننا فرض ذلك إذا تم تجاوز أي عنصر ، فيجب أن يتم ذلك جميعًا؟ سيسمح لنا ذلك بالتفكير بناءً على النوع الدقيق لنوع الارتباط الافتراضي (على سبيل المثال) في العناصر الأخرى ، والذي يبدو وكأنه تعزيز مفيد في التعبير.
هل يجب السماح بما يلي؟ أريد أن تخصص نوعًا ما بحيث يكون ArrayVec هو Copy
عندما يكون نوع العنصر الخاص به هو Copy ، وأن يكون له مدمر. أحاول تحقيقه باستخدام مجال داخلي يحل محله التخصص.
كنت آمل أن يتم تجميع هذا ، أي أنه يستنتج إمكانية نسخ حقول ArrayVec<A>
من أنواع الحقول التي تم تحديدها بواسطة A: Copy + Array
bound (مقتطف قابل للترجمة في الملعب) .
impl<A: Copy + Array> Copy for ArrayVec<A>
//where <A as Repr>::Data: Copy
{ }
بند التعليق الذي تم التعليق عليه غير مطلوب لأنه يعرض نوعًا خاصًا Repr
في الواجهة العامة. (إنه أيضًا ICEs على أي حال).
تحرير: لقد نسيت أنني أبلغت عن المشكلة رقم 33162 حول هذا بالفعل ، أنا آسف.
متابعة تعليقي ، حالة الاستخدام الفعلي:
// Ideal version
trait Scannable {}
impl<T: FromStr> Scannable for T {}
impl<T: FromStr> Scannable for Result<T, ()> {}
// But this doesn't follow from the specialisation rules because Result: !FromStr
// Lattice rule would allow filling in that gap or negative reasoning would allow specifying it.
// Second attempt
trait FromResult {
type Ok;
fn from(r: Result<Self::Ok, ()>) -> Self;
}
impl<T> Scannable for T {
default type Ok = T;
default fn from(r: Result<T, ()>) -> Self {...} // error can't assume Ok == T, could do this if we had `default impl`
}
impl<T> Scannable for Result<T, ()> {
type Ok = T;
default fn from(r: Result<T, ()>) -> Self { r }
}
fn scan_from_str<T: FromResult>(x: &str) -> T
where <T as FromResult>::Ok: FromStr // Doesn't hold for T: FromStr because of the default on T::Ok
{ ... }
// Can also add the FromStr bound to FromResult::Ok, but doesn't help
// Third attempt
trait FromResult<Ok> {
fn from(r: Result<Ok, ()>) -> Self;
}
impl<T> FromResult<T> for T {
default fn from(r: Result<Self, ()>) -> Self { ... }
}
impl<T> FromResult<T> for Result<T, ()> {
fn from(r: Result<T, ())>) -> Self { r }
}
fn scan_from_str<U: FromStr, T: FromResult<U>>(x: &str) -> T { ... }
// Error because we can't infer that U == String
let mut x: Result<String, ()> = scan_from_str("dsfsf");
تضمين التغريدة
المشكلة هناك أنه لا يُسمح لك بالاعتماد على قيمة العناصر الافتراضية الأخرى. لذلك عندما يكون لديك هذا الضمني:
impl<T> ClonableIterator for T where T: Iterator {
default type ClonableIter = VecIntoIter<T::Item>;
default fn clonable(self) -> VecIntoIter<T::Item> {
// ^^^^^^^^^^^^^^^^^^^^
self.collect::<Vec<_>>().into_iter()
}
}
في المكان الذي أبرزته ، يعتمد clonable
على Self::ClonableIter
، ولكن نظرًا لأنه تم الإعلان عن CloneableIter
كإعداد افتراضي ، لا يمكنك فعل ذلك. القلق هو أن شخصًا ما قد يتخصص ويتجاوز CloneableIter
لكن _not_ clonable
.
لقد تحدثنا عن بعض الإجابات المحتملة هنا. كان أحدها هو السماح لك باستخدام default
لتجميع العناصر معًا حيث ، إذا تجاوزت أحدها ، فيجب عليك تجاوز الكل:
impl<T> ClonableIterator for T where T: Iterator {
default {
type ClonableIter = VecIntoIter<T::Item>;
fn clonable(self) -> VecIntoIter<T::Item> { ... }
}
}
هذا جيد ، لكن قليلاً "تحفيز الانجراف إلى اليمين". يبدو default
أيضًا وكأنه نطاق تسمية ، وهو ليس كذلك. قد يكون هناك متغير أبسط يتيح لك فقط التبديل بين "تجاوز أي" (كما هو الحال اليوم) مقابل "تجاوز الكل" (ما تحتاجه).
كنا نأمل أيضًا أن نتمكن من الحصول عليها من خلال الاستفادة من impl Trait
. ستكون الفكرة أن هذا غالبًا ما يظهر ، كما هو الحال هنا ، عندما تريد تخصيص نوع الإرجاع من الطرق. لذلك ربما إذا كان بإمكانك إعادة كتابة السمة لاستخدام impl Trait
:
pub trait ClonableIterator: Iterator {
fn clonable(self) -> impl Iterator;
}
سيكون هذا نوعًا من الاختزال الفعال عند تنفيذه لمجموعة افتراضية تحتوي على النوع و fn. (لست متأكدًا مما إذا كانت هناك طريقة للقيام بذلك فقط في الضمانات.)
ملاحظة ، آسف للتأخير الطويل في الرد على رسائلك ، والتي أراها من تاريخ _ يوليو.
بينما تساعد سمة التضمين ، لا يوجد RFC تم قبوله أو تنفيذه مما يسمح باستخدامه مع أجسام السمات بأي شكل ، لذا فإن النظر إليها من أجل RFC هذا يبدو غريبًا بعض الشيء.
أنا مهتم بتنفيذ ميزة default impl
(حيث تكون جميع العناصر default
).
هل تقبل المساهمة في ذلك؟
تضمين التغريدة سأكون سعيدًا للمساعدة في توجيه العمل أيضًا.
هل يوجد حاليًا استنتاج حول ما إذا كان يجب أن تكون الأنواع المرتبطة قابلة للتخصص؟
فيما يلي تبسيط لحالة الاستخدام الخاصة بي ، مما يدل على الحاجة إلى أنواع مرتبطة قابلة للتخصص.
لدي بنية بيانات عامة ، على سبيل المثال Foo
، والتي تنسق مجموعة من كائنات سمة الحاوية ( &trait::Property
). يتم تنفيذ السمة trait::Property
بواسطة كل من Property<T>
(مدعوم بـ Vec<T>
) و PropertyBits
(مدعوم بـ BitVec
، متجه قليلاً).
في الأساليب العامة على Foo
، أود أن أكون قادرًا على تحديد بنية البيانات الأساسية الصحيحة لـ T
عبر الأنواع المرتبطة ، لكن هذا يتطلب التخصص للحصول على ضمانة شاملة للحالات غير الخاصة مثل يتبع.
trait ContainerFor {
type P: trait::Property;
}
impl<T> ContainerFor for T {
default type P = Property<T>; // default to the `Vec`-based version
}
impl ContainerFor for bool {
type P = PropertyBits; // specialize to optimize for space
}
impl Foo {
fn add<T>(&mut self, name: &str) {
self.add_trait_obj(name, Box::new(<T as ContainerFor>::P::new())));
}
fn get<T>(&mut self, name: &str) -> Option<&<T as ContainerFor>::P> {
self.get_trait_obj(name).and_then(|prop| prop.downcast::<_>());
}
}
شكرا aturon !
في الأساس ، أقوم بهذا العمل عن طريق إضافة سمة "تقصير" جديدة إلى هيكل ast::ItemKind::Impl
(ثم استخدم السمة الجديدة جنبًا إلى جنب مع سمة "defaultness" العنصر الضمني) ولكن هناك أيضًا خاصية سريعة وسهلة
الاحتمال الذي يتكون من تعيين افتراضي لجميع العناصر الضمنية لـ default impl
أثناء التحليل.
بالنسبة لي ، هذا ليس حلاً "كاملاً" لأننا فقدنا المعلومات التي تفيد بأن "التقصير" مرتبط بالضمانات وليس بكل عنصر ضمني ،
بالإضافة إلى ذلك ، إذا كانت هناك خطة لتقديم partial impl
فإن الحل الأول سيوفر بالفعل سمة يمكن استخدامها لتخزين default
بالإضافة إلى partial
. ولكن فقط للتأكد و
لا تضيع الوقت ، ما رأيك؟
giannicicaturon هل يمكنني اقتراح إنشاء مشكلة معينة لمناقشة default impl
؟
لا تهتم ، لقد قمت بإنشاء واحد: https://github.com/rust-lang/rust/issues/37653
هل تسمح لي قاعدة الشبكة ، بالنظر إلى:
trait Foo {}
trait A {}
trait B {}
trait C {}
// ...
أضف تطبيقات Foo
لمجموعة فرعية من الأنواع التي تنفذ مجموعة من A
، B
، C
، ...:
impl Foo for T where T: A { ... }
impl Foo for T where T: B { ... }
impl Foo for T where T: A + B { ... }
impl Foo for T where T: B + C { ... }
// ...
واسمحوا لي "بمنع" بعض التركيبات ، على سبيل المثال ، أن A + C
يجب ألا يحدث أبدًا:
impl Foo for T where T: A + C = delete;
؟
السياق: لقد رغبت في ذلك عند تطبيق سمة ApproxEqual(Shape, Shape)
لأنواع مختلفة من الأشكال (النقاط ، المكعبات ، المضلعات ، ...) حيث تكون هذه كلها سمات. اضطررت إلى التغلب على هذا من خلال إعادة هيكلة هذا إلى سمات مختلفة ، على سبيل المثال ، ApproxEqualPoint(Point, Point)
، لتجنب عمليات التنفيذ المتضاربة.
تضمين التغريدة
واسمحوا لي "بمنع" بعض التركيبات ، على سبيل المثال ، أن A + C يجب ألا يحدث أبدًا:
لا ، هذا ليس شيئًا تسمح به قاعدة الشبكة. سيكون ذلك مجال "التفكير السلبي" بشكل أو بآخر.
السياق: لقد رغبت في ذلك عند تنفيذ سمة ApproxEqual (الشكل ، الشكل) لأنواع مختلفة من الأشكال (النقاط ، المكعبات ، المضلعات ، ...) حيث تكون هذه كلها سمات. كان علي أن أتغلب على هذا من خلال إعادة هيكلة هذا في سمات مختلفة ، على سبيل المثال ، ApproxEqualPoint (نقطة ، نقطة) ، لتجنب عمليات التنفيذ المتضاربة.
لذا ، روجwithoutboats لفكرة "مجموعات الاستبعاد" ، حيث يمكنك التصريح بأن مجموعة معينة من السمات متعارضة (على سبيل المثال ، يمكنك تنفيذ واحدة منها على الأكثر). أتصور هذا نوعًا من كونه مثل التعداد (أي ، يتم الإعلان عن السمات معًا). تعجبني فكرة هذا ، خاصة أنه (أعتقد!) يساعد على تجنب بعض الجوانب الأكثر ضررًا للتفكير السلبي. لكني أشعر أن هناك حاجة لمزيد من التفكير في هذه الجبهة - وأيضًا كتابة جيدة تحاول تلخيص جميع "البيانات" التي تدور حول كيفية التفكير في التفكير السلبي. ربما الآن بعد أن انتهيت (في الغالب) من سلسلة HKT والتخصصات ، يمكنني التفكير في ذلك ...
@ nikomatsakis :
لذا ، روجwithoutboats لفكرة "مجموعات الاستبعاد" ، حيث يمكنك التصريح بأن مجموعة معينة من السمات متعارضة (على سبيل المثال ، يمكنك تنفيذ واحدة منها على الأكثر). أتصور هذا نوعًا من كونه مثل التعداد (أي ، يتم الإعلان عن السمات معًا). تعجبني فكرة هذا ، خاصة أنه (أعتقد!) يساعد على تجنب بعض الجوانب الأكثر ضررًا للتفكير السلبي. لكني أشعر أن هناك حاجة لمزيد من التفكير في هذه الجبهة - وأيضًا كتابة جيدة تحاول تلخيص جميع "البيانات" التي تدور حول كيفية التفكير في التفكير السلبي. ربما الآن بعد أن انتهيت (في الغالب) من سلسلة HKT والتخصصات ، يمكنني التفكير في ذلك ...
فكرت في مجموعات الاستثناءات أثناء كتابة هذا (لقد ذكرتها في المنتديات في اليوم الآخر) ، لكنني لا أعتقد أنها يمكن أن تعمل لأنه في هذا المثال بالذات ، ليست كل تطبيقات السمات حصرية. المثال الأكثر تافهًا هو السمات Point
و Float
: يمكن أن تكون Float
_ نقطة 1D ، لذلك لا يمكن أن تكون ApproxEqualPoint(Point, Point)
و ApproxEqualFloat(Float, Float)
حصرية. هناك أمثلة أخرى مثل Square
و Polygon
أو Box
| Cube
و AABB
(المربع المحيط بمحاذاة المحور) حيث يحتاج "التسلسل الهرمي للسمات" في الواقع إلى قيود أكثر تعقيدًا.
لا ، هذا ليس شيئًا تسمح به قاعدة الشبكة. سيكون ذلك مجال "التفكير السلبي" بشكل أو بآخر.
سأكون قادرًا على الأقل على تنفيذ الحالة المعينة ووضع unimplemented!()
فيها. سيكون ذلك كافيًا ، لكن من الواضح أنني أرغب في ذلك أكثر إذا كان المترجم سيكتشف بشكل ثابت تلك الحالات التي أستدعي فيها دالة بداخلها unimplemented!()
(وفي هذه المرحلة ، نحن مرة أخرى في منطقة التفكير السلبي) .
سيسمح لك التخصص الشبكي gnzlbg بإثارة الذعر الضمني ، لكن فكرة القيام بذلك تجعلني: أبكي :.
إن فكرة "مجموعات الاستبعاد" هي في الحقيقة مجرد حدود سالبة فوق الصورة. هناك شيء واحد لم نستكشفه جيدًا وهو فكرة تخصص القطبية العكسية - مما يسمح لك بكتابة ضمنية متخصصة ذات قطبية معكوسة إلى ضمنتها الأقل تخصصًا. على سبيل المثال ، في هذه الحالة ستكتب فقط:
impl<T> !Foo for T where T: A + C { }
لست متأكدًا تمامًا من الآثار المترتبة على السماح بذلك. أعتقد أنه يتعلق بالمسائل التي أبرزها نيكو بالفعل حول كيف أن التخصص نوع من الخلط بين إعادة استخدام الشفرة وتعدد الأشكال في الوقت الحالي.
مع كل هذا النقاش حول الاستدلال السلبي والتلميحات السلبية ، أشعر بأنني مضطر لإحضار فكرة هاسكل القديمة عن "سلاسل المثيلات" مرة أخرى ( ورقة ، ورقة ، أداة تعقب مشكلات GHC ، Rust pre-RFC ) ، كمصدر محتمل للإلهام إذا لم يكن هناك شيء آخر.
الفكرة الأساسية هي أنه في أي مكان يمكنك كتابة ضمنية trait
، يمكنك أيضًا كتابة أي عدد من "الجمل الأخرى إذا كانت" تحدد قيمة impl
والتي يجب أن تنطبق في حالة السابقة لم يتم ذلك ، مع وجود "جملة أخرى" اختيارية تحدد ضمنيًا سلبيًا (أي ، إذا لم يتم تطبيق أي من البنود الخاصة بـ Trait
، فسيتم تطبيق !Trait
)
تضمين التغريدة
إن فكرة "مجموعات الاستبعاد" هي في الحقيقة مجرد حدود سالبة فوق الصورة.
أعتقد أن هذا سيكون كافيًا لحالات الاستخدام الخاصة بي.
أعتقد أنه يتعلق بالمسائل التي أبرزها نيكو بالفعل حول كيف أن التخصص نوع من الخلط بين إعادة استخدام الشفرة وتعدد الأشكال في الوقت الحالي.
لا أعرف ما إذا كان من الممكن حل هذه الأمور. أريد أن أمتلك:
impl
s ليتم تجميعها.تغطية جميع الحالات صعب ، لكن إذا أرغمني المترجم على تغطية جميع الحالات:
trait Foo {}
trait A {}
trait B {}
impl<T> Foo for T where T: A { ... }
impl<T> Foo for T where T: B { ... }
// impl<T> Foo for T where T: A + B { ... } //< compiler: need to add this impl!
ويعطيني أيضًا إشارات سلبية:
impl<T> !Foo for T where T: A + B { }
impl<T> !Foo for T where T: _ { } // _ => all cases not explicitly covered yet
سأكون قادرًا على إضافة الضمانات بشكل تدريجي كما أحتاجها وأيضًا الحصول على أخطاء مترجم لطيفة عندما أحاول استخدام سمة بنوع لا يوجد ضمني له.
لست متأكدًا تمامًا من الآثار المترتبة على السماح بذلك.
ذكر نيكو أن هناك مشاكل في التفكير السلبي. FWIW الشيء الوحيد الذي يتم استخدام المنطق السلبي له في المثال أعلاه هو توضيح أن المستخدم يعرف أن هناك حاجة إلى ضمني لحالة معينة ، ولكنه قرر صراحة عدم توفير تطبيق له.
لقد قمت للتو بالضغط على # 33017 ولم أره مرتبطًا هنا حتى الآن. تم وضع علامة على أنها ثقب سلامة لذا سيكون من الجيد تتبعها هنا.
بالنسبة إلى https://github.com/dtolnay/quote/issues/7 ، أحتاج إلى شيء مشابه لهذا المثال من RFC الذي لا يعمل بعد. سم مكعبtomakaAatchrphmeier الذي علق عن هذا في وقت سابق.
trait Example {
type Output;
fn generate(self) -> Self::Output;
}
impl<T> Example for T {
default type Output = Box<T>;
default fn generate(self) -> Box<T> { Box::new(self) }
}
impl Example for bool {
type Output = bool;
fn generate(self) -> bool { self }
}
لقد عثرت على الحل التالي الذي يعطي طريقة للتعبير عن نفس الشيء.
#![feature(specialization)]
use std::fmt::{self, Debug};
///////////////////////////////////////////////////////////////////////////////
trait Example: Output {
fn generate(self) -> Self::Output;
}
/// In its own trait for reasons, presumably.
trait Output {
type Output: Debug + Valid<Self>;
}
fn main() {
// true
println!("{:?}", Example::generate(true));
// box("s")
println!("{:?}", Example::generate("s"));
}
///////////////////////////////////////////////////////////////////////////////
/// Instead of `Box<T>` just so the "{:?}" in main() clearly shows the type.
struct MyBox<T: ?Sized>(Box<T>);
impl<T: ?Sized> Debug for MyBox<T>
where T: Debug
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "box({:?})", self.0)
}
}
///////////////////////////////////////////////////////////////////////////////
/// Return type of the impl containing `default fn`.
type DefaultOutput<T> = MyBox<T>;
impl Output for bool {
type Output = bool;
}
impl<T> Example for T where T: Pass {
default fn generate(self) -> Self::Output {
T::pass({
// This is the impl you wish you could write
MyBox(Box::new(self))
})
}
}
impl Example for bool {
fn generate(self) -> Self::Output {
self
}
}
///////////////////////////////////////////////////////////////////////////////
// Magic? Soundness exploit? Who knows?
impl<T: ?Sized> Output for T where T: Debug {
default type Output = DefaultOutput<T>;
}
trait Valid<T: ?Sized> {
fn valid(DefaultOutput<T>) -> Self;
}
impl<T: ?Sized> Valid<T> for DefaultOutput<T> {
fn valid(ret: DefaultOutput<T>) -> Self {
ret
}
}
impl<T> Valid<T> for T {
fn valid(_: DefaultOutput<T>) -> Self {
unreachable!()
}
}
trait Pass: Debug {
fn pass(DefaultOutput<Self>) -> <Self as Output>::Output;
}
impl<T: ?Sized> Pass for T where T: Debug, <T as Output>::Output: Valid<T> {
fn pass(ret: DefaultOutput<T>) -> <T as Output>::Output {
<T as Output>::Output::valid(ret)
}
}
ما زلت أعمل على https://github.com/dtolnay/quote/issues/7 وأحتاج إلى نمط ماسي. هذا هو الحل الخاص بي. cczitsen الذي سأل عن هذا سابقًا و aturon و @ rphmeier الذين ردوا.
#![feature(specialization)]
/// Can't have these impls directly:
///
/// - impl<T> Trait for T
/// - impl<T> Trait for T where T: Clone
/// - impl<T> Trait for T where T: Default
/// - impl<T> Trait for T where T: Clone + Default
trait Trait {
fn print(&self);
}
fn main() {
struct A;
A.print(); // "neither"
#[derive(Clone)]
struct B;
B.print(); // "clone"
#[derive(Default)]
struct C;
C.print(); // "default"
#[derive(Clone, Default)]
struct D;
D.print(); // "clone + default"
}
trait IfClone: Clone { fn if_clone(&self); }
trait IfNotClone { fn if_not_clone(&self); }
impl<T> Trait for T {
default fn print(&self) {
self.if_not_clone();
}
}
impl<T> Trait for T where T: Clone {
fn print(&self) {
self.if_clone();
}
}
impl<T> IfClone for T where T: Clone {
default fn if_clone(&self) {
self.clone();
println!("clone");
}
}
impl<T> IfClone for T where T: Clone + Default {
fn if_clone(&self) {
self.clone();
Self::default();
println!("clone + default");
}
}
impl<T> IfNotClone for T {
default fn if_not_clone(&self) {
println!("neither");
}
}
impl<T> IfNotClone for T where T: Default {
fn if_not_clone(&self) {
Self::default();
println!("default");
}
}
ضرب خطأ (أو على الأقل سلوك غير متوقع من وجهة نظري) مع التخصص واكتب الاستدلال: # 38167
يجب توقع أن يكون هذان الوسيلان صالحين مع التخصص ، أليس كذلك؟ يبدو أنه لم يتم التقاطه بنجاح.
impl<T, ST, DB> ToSql<Nullable<ST>, DB> for T where
T: ToSql<ST, DB>,
DB: Backend + HasSqlType<ST>,
ST: NotNull,
{
...
}
impl<T, ST, DB> ToSql<Nullable<ST>, DB> for Option<T> where
T: ToSql<ST, DB>,
DB: Backend + HasSqlType<ST>,
ST: NotNull,
{
...
}
لقد قدمت https://github.com/rust-lang/rust/issues/38516 لبعض السلوك غير المتوقع الذي صادفته أثناء العمل على بناء التخصص في Serde. على غرار https://github.com/rust-lang/rust/issues/38167 ، هذه هي الحالة التي يتم فيها تجميع البرنامج بدون التضمين المتخصص وعندما يتم إضافته يوجد خطأ في النوع. ccbluss الذي كان قلقًا بشأن هذا الموقف سابقًا.
ماذا لو سمحنا بالتخصص بدون الكلمة الرئيسية default
داخل صندوق واحد ، على غرار الطريقة التي نسمح بها بالتفكير السلبي داخل صندوق واحد؟
تبريري الرئيسي هو هذا: "نمط التكرارات والمتجهات." في بعض الأحيان ، يرغب المستخدمون في تنفيذ شيء ما لجميع التكرارات والمتجهات:
impl<I> Foo for I where I: Iterator<Item = u32> { ... }
impl Foo for Vec<u32> { ... }
(هذا ذو صلة بمواقف أخرى غير التكرارات والمتجهات ، بالطبع ، هذا مجرد مثال واحد).
اليوم هذا لا يتراكم ، وهناك تسوس وصرير الأسنان. التخصص يحل هذه المشكلة:
default impl<I> Foo for I where I: Iterator<Item = u32> { ... }
impl Foo for Vec<u32> { ... }
لكن في حل هذه المشكلة ، أضفت عقدًا عامًا إلى صندوقك: من الممكن تجاوز الضمانة التكرارية لـ Foo
. ربما لا نريد إجبارك على القيام بذلك - وبالتالي ، التخصص المحلي بدون default
.
السؤال الذي أفترضه هو ، ما هو بالضبط دور default
. أعتقد أن طلب default
كان في الأصل إيماءة تجاه التفسير والتوثيق الذاتي للكود. تمامًا كما أن كود Rust غير قابل للتغيير افتراضيًا ، خاص بشكل افتراضي ، آمن افتراضيًا ، يجب أيضًا أن يكون نهائيًا بشكل افتراضي. ومع ذلك، لأنه "غير نهائية" هي خاصية العالمية، لا أستطيع أن تتخصص عنصر ما لم اسمحوا لي ان كنت متخصصون عنصر.
أعتقد أن طلب
default
كان في الأصل إيماءة نحو توضيح التعليمات البرمجية والتوثيق الذاتي. ومع ذلك ، [..] لا يمكنني تخصيص عنصر إلا إذا سمحت لك بتخصيص عنصر ما.
هل هذا حقا سيء جدا مع ذلك؟ إذا كنت ترغب في التخصص في ضمني ، فربما يريد الآخرون أيضًا.
أشعر بالقلق لأن مجرد التفكير في RFC هذا يعطيني بالفعل ذكريات PTSD من العمل في قواعد C ++ البرمجية التي تستخدم كميات فاحشة من التحميل الزائد والميراث وليس لديها أي فكرة عن أن wtf يحدث في أي سطر من الكود الذي يحتوي على طريقة استدعاء فيه. إنني أقدر حقًا الجهود التي بذلتهاaturon لتوضيح التخصص والتوثيق الذاتي.
هل هذا حقا سيء جدا مع ذلك؟ إذا كنت ترغب في التخصص في ضمني ، فربما يريد الآخرون أيضًا.
إذا كان هناك أشخاص آخرون يريدون "ربما" التخصص في ذلك أيضًا ، وإذا كانت هناك حالات جيدة لا نريدهم أن يفعلوها ، فلا يجب أن نجعل من المستحيل تحديد ذلك. (يشبه إلى حد ما التغليف: تريد الوصول إلى بعض البيانات وربما يرغب بعض الأشخاص الآخرين في ذلك أيضًا - لذلك تضع علامة صراحة على هذه البيانات علنية ، بدلاً من جعل جميع البيانات عامة بشكل افتراضي.)
أشعر بالقلق لأن مجرد التفكير في RFC هذا يعطيني بالفعل ذكريات من اضطراب ما بعد الصدمة ...
ولكن كيف يمنع رفض هذا التخصيص حدوث هذه الأشياء؟
إذا كانت هناك حالات جيدة حيث لا نريدهم أن يفعلوا ذلك ، فلا يجب أن نجعل من المستحيل تحديد ذلك.
ليس من الجيد بالضرورة أن تمنح المستخدمين القوة متى كان لديهم استخدام جيد لها. ليس إذا كان يمكّن المستخدمين أيضًا من كتابة كود محير.
ولكن كيف يمنع رفض هذا التخصيص حدوث هذه الأشياء؟
لنفترض أنك رأيت foo.bar()
وتريد إلقاء نظرة على ما يفعله bar()
. في الوقت الحالي ، إذا وجدت الطريقة التي تم تنفيذها على نوع مطابق ولم يتم وضع علامة عليها default
فأنت تعلم أن هذا هو تعريف الطريقة الذي تبحث عنه. مع اقتراح withoutboats ، لن يكون هذا صحيحًا - بدلاً من ذلك لن تعرف أبدًا على وجه اليقين ما إذا كنت تبحث بالفعل في الكود الذي يتم تنفيذه.
بدلاً من ذلك ، لن تعرف أبدًا على وجه اليقين ما إذا كنت تنظر بالفعل إلى الكود الذي يتم تنفيذه.
هذه مبالغة كبيرة في تأثير السماح بتخصص الضمانات غير الافتراضية للأنواع المحلية. إذا كنت تنظر إلى ضمني ملموس ، فأنت تعلم أنك تنظر إلى الضمانة الصحيحة. ولديك حق الوصول إلى المصدر الكامل لهذا الصندوق ؛ يمكنك تحديد ما إذا كان هذا الضمني متخصصًا أم لا بأسرع من "أبدًا".
في غضون ذلك ، حتى مع default
، تظل المشكلة قائمة عندما لا يتم إنهاء الضميمة. إذا كان الضمني الصحيح هو ضمني default
، فأنت في نفس الموقف حيث تواجه صعوبة في عدم التأكد مما إذا كان هذا هو الضمني الصحيح. وبالطبع إذا تم توظيف التخصص ، فسيكون هذا هو الحال بشكل شائع (على سبيل المثال ، هذا هو الحال اليوم تقريبًا لكل أداة من ToString
).
في الحقيقة أعتقد أن هذه مشكلة خطيرة إلى حد ما ، لكنني لست مقتنعًا بأن default
يحلها. ما نحتاجه هو أدوات تنقل شفرة أفضل. يبذل rustdoc حاليًا نهجًا "أفضل جهد" عندما يتعلق الأمر بالسمات الضمنية - فهو لا يرتبط بمصدرها ولا حتى يسرد الضمانات التي توفرها الضمانات الشاملة.
أنا لا أقول أن هذا التغيير هو ضربة قاسية بأي حال من الأحوال ، لكنني أعتقد أنه يستحق اعتبارًا أكثر دقة.
ليس من الجيد بالضرورة أن تمنح المستخدمين القوة متى كان لديهم استخدام جيد لها. ليس إذا كان يمكّن المستخدمين أيضًا من كتابة كود محير.
بالضبط ، أنا أوافق تمامًا. أعتقد أنني أتحدث عن "مستخدم" مختلف هنا ، وهو مستخدم الصناديق التي تكتبها. لا تريدهم أن يتخصصوا بحرية في السمات في قفصك (ربما يؤثر ذلك على سلوك قفصك بطريقة متسللة). من ناحية أخرى ، سنمنح "المستخدم" الذي تتحدث عنه مزيدًا من القوة ، أي مؤلف الصندوق ، ولكن حتى بدون اقتراح وواجه نفس المشكلة .
أعتقد أن default
يساعد بمعنى أنه إذا كنت ترغب في تبسيط قراءة رمز ما ، فيمكنك أن تطلب عدم استخدام أي شخص default
أو وضع قواعد توثيق صارمة لاستخدامه. في هذه المرحلة ، ما عليك سوى القلق بشأن default
s من std
، والتي من المفترض أن يفهمها الناس بشكل أفضل.
أتذكر فكرة أن قواعد التوثيق يمكن فرضها على استخدامات التخصص التي ساهمت في الحصول على التخصص الذي تمت الموافقة عليه من قبل RFC.
withoutboats ، هل أنا محق في قراءة default
لأنك تريد نموذجًا مقيدًا default
والذي يعني "قابل للتجاوز ، ولكن فقط في هذا الصندوق" (أي ، pub(crate)
لكن مقابل default
)؟ ومع ذلك ، لتبسيط الأمور ، فأنت تقترح تغيير دلالات حذف default
، بدلاً من إضافة التخرج من default
-ness؟
صيح. يبدو أن القيام بشيء مثل default(crate)
مبالغ فيه.
بداهة ، أتخيل أنه يمكن للمرء محاكاة ذلك من خلال ما يصدره الصندوق ، أليس كذلك؟ هل هناك أي مواقف لا يمكنك فيها ببساطة تقديم سمة مساعدة خاصة باستخدام طرق default
واستدعائها من impl
s الأخير الخاص بك؟ هل تريد أن يستخدم المستخدم الخاص بك default
s لكن لا يقدم أيًا من منتجاته؟
صيح. إن القيام بشيء مثل الافتراضي (الصندوق) يبدو وكأنه مبالغة.
أنا أعترض. اريد حقا شكل مقيد من التقصير. كنت أقصد اقتراحه. حافزي هو أنه في بعض الأحيان يشير التقاطع إلى ما سيجبرك على إضافة افتراضي ، لكن هذا لا يعني أنك تريد السماح للصناديق التعسفية بتغيير سلوكك. آسف ، لدي اجتماع ، يمكنني أن أحاول الإسهاب بمثال بعد قليل.
nikomatsakis لدي نفس الدافع ، ما أقترحه هو أننا فقط نزيل الشرط الافتراضي للتخصص في نفس الصندوق ، بدلاً من إضافة المزيد من الرافعات. :-)
إذا كان من قبيل الصدفة أن يكون هذا الإعداد الافتراضي غير المُصدر هو الاستخدام الأكثر شيوعًا ، فسيكون من الأسهل تذكر ميزة #[default_export]
عن طريق القياس مع #[macro_export]
. قد يكون الخيار الوسيط هو السماح لميزة التصدير هذه لخطوط pub use
أو pub mod
.
سيكون استخدام الكلمة الأساسية pub
أفضل ، لأن وحدات الماكرو 2.0 ستدعم وحدات الماكرو كعناصر عادية وستستخدم pub
بدلاً من #[macro_use]
. سيكون استخدام pub
للإشارة إلى الرؤية عبر اللوحة بمثابة فوز كبير لاتساقها.
withoutboats بغض النظر ، أعتقد أنك سترغب أحيانًا في التخصص محليًا ولكن ليس بالضرورة فتح الأبواب للجميع
استخدام الكلمة الرئيسية العامة سيكون أفضل
إن وجود pub default fn
يعني "تصدير تقصير fn علنًا" بدلاً من التأثير على رؤية الوظيفة نفسها سيكون أمرًا مربكًا للغاية للقادمين الجدد.
jimmycuadra هل هذا ما قصدته باستخدام الكلمة الأساسية pub
؟ أتفق مع sgrif على أنه يبدو أكثر إرباكًا ، وإذا كنا سنسمح لك بتحديد نطاق التقصير بشكل صريح ، فإن نفس الصيغة التي قررناها لتحديد نطاق الرؤية تبدو أنها المسار الصحيح.
ربما ليس pub default fn
بالضبط ، لأن هذا أمر غامض ، كما ذكرت. كنت أقول فقط أن هناك قيمة لامتلاك pub
يعني بشكل عام "فضح شيء خاص إلى الخارج". من المحتمل أن تكون هناك بعض الصيغ النحوية التي تتضمن pub
والتي ستكون مختلفة بصريًا حتى لا يتم الخلط بينها وبين جعل الوظيفة نفسها عامة.
على الرغم من أنها بناء جملة إلى حد ما ، إلا أنني لن أعارض عمل default(foo)
مثل pub(foo)
- فالتناظر بين الاثنين يفوق بشكل هامشي العبث في بناء الجملة بالنسبة لي.
تحذير من ركوب الدراجة: هل فكرنا في تسميته overridable
بدلاً من default
؟ إنها وصفيّة أكثر ، و overridable(foo)
تقرأ أفضل من default(foo)
- يقترح الأخير "هذا هو الخيار الافتراضي ضمن النطاق foo
، ولكن قد يكون هناك شيء آخر هو الافتراضي في مكان آخر "، بينما يقول السابق" هذا يمكن تجاوزه في نطاق foo
"، وهذا صحيح.
أعتقد أن السؤالين الأولين هما حقًا: هل تصدير أو عدم تصدير default
ness أكثر شيوعًا بشكل ملحوظ؟ ألا يجب أن default
ness هو السلوك الافتراضي؟
burdges هل لديك تسميات "نعم حالة" و "بلا حالة" للخلف ، أم أنني أسيء فهم ما تقوله؟
نعم عفوا! ثابت!
لدينا impl<T> Borrow<T> for T where T: ?Sized
بحيث يمكن لربط Borrow<T>
التعامل مع القيم المملوكة كما لو تم استعارتها.
أفترض أنه يمكننا استخدام التخصص لتحسين المكالمات الخارجية إلى clone
من Borrow<T>
، أليس كذلك؟
pub trait CloneOrTake<T> {
fn clone_or_take(self) -> T;
}
impl<B,T> CloneOrTake<T> for B where B: Borrow<T>, T: Clone {
#[inline]
default fn clone_or_take(b: B) -> T { b.clone() }
}
impl<T> CloneOrTake<T> for T {
#[inline]
fn clone_or_take(b: T) -> T { b };
}
أعتقد أن هذا قد يجعل Borrow<T>
قابلة للاستخدام في مواقف أكثر. لقد أسقطت الحد T: ?Sized
لأنه من المفترض أن يحتاج المرء Sized
عند إرجاع T
.
قد يكون نهج آخر
pub trait ToOwnedFinal : ToOwned {
fn to_owned_final(self) -> Self::Owned;
}
impl<B> ToOwnedFinal for B where B: ToOwned {
#[inline]
default fn to_owned_final(b: B) -> Self::Owned { b.to_owned() }
}
impl<T> ToOwnedFinal for T {
#[inline]
fn to_owned_final(b: T) -> T { b };
}
لقد حققنا بعض الاكتشافات التي قد تكون مقلقة اليوم ، يمكنك قراءة سجلات IRC هنا: https://botbot.me/mozilla/rust-lang/
لست واثقًا بنسبة 100٪ من جميع الاستنتاجات التي توصلنا إليها ، خاصة وأن تعليقات نيكو بعد الحقيقة تبدو مشجعة. لفترة قصيرة بدا لي الأمر مروعًا بعض الشيء.
هناك شيء واحد أشعر بالتأكد منه إلى حد ما وهو أن طلب default
لا يمكن جعله متوافقًا مع ضمان أن إضافة أدوات default
متوافقة دائمًا مع الإصدارات السابقة. ها هي المظاهرة:
parent
v 1.0.0
trait A { }
trait B { }
trait C {
fn foo(&self);
}
impl<T> C for T where T: B {
// No default, not specializable!
fn foo(&self) { panic!() }
}
client
(يعتمد على parent
)extern crate parent;
struct Local;
impl parent::A for Local { }
impl parent::C for Local {
fn foo(&self) { }
}
تنفذ المحلية A
و C
لكن ليس B
. إذا تم تنفيذ B
محليًا ، فستتعارض ضمنته C
مع الضمانة الشاملة غير القابلة للتخصيص وهي C for T where T: B
.
parent
v 1.1.0
// Same code as before, but add:
default impl<T> B for T where T: A { }
تمت إضافة هذا الضمني ، وهو عبارة عن ضمني متخصص تمامًا ، لذلك قلنا أنه تغيير غير منقطع. ومع ذلك ، فإنه ينشئ ضمنيًا متعدِّدًا - لدينا بالفعل "كل B impl C (غير قابل للتخصيص)" ، بإضافة "all A impl B (خاص)" ، لقد أضفنا ضمنيًا العبارة "all A impl C (غير قابل للتخصيص) ". الآن لا يمكن ترقية صندوق الطفل.
قد يكون الأمر كذلك أن فكرة ضمان أن إضافة الضمانات المتخصصة ليست تغييرًا متقطعًا قد خرجت تمامًا من النافذة ، لأن آرون أظهر (كما ترون في السجلات المرتبطة أعلاه) أنه يمكنك كتابة الضمانات التي تقدم ضمانات مكافئة فيما يتعلق بالتقصير . ومع ذلك ، تشير تعليقات نيكو اللاحقة إلى أن مثل هذه الضمانات قد تكون محظورة (أو على الأقل ممنوعة) بموجب القواعد اليتيمة.
لذلك من غير مؤكد بالنسبة لي لو كان 'impls هي غير منقسمة "الضمان هو انقاذها، ولكن من المؤكد أنه غير متوافق مع سيطرة واضحة على مدى نهائية impl.
هل هناك أي خطة للسماح بذلك؟
struct Foo;
trait Bar {
fn bar<T: Read>(stream: &T);
}
impl Bar for Foo {
fn bar<T: Read>(stream: &T) {
let stream = BufReader::new(stream);
// Work with stream
}
fn bar<T: BufRead>(stream: &T) {
// Work with stream
}
}
لذلك ، بشكل أساسي تخصص لوظيفة القالب التي تحتوي على معلمة نوع مرتبطة بـ A
حيث يكون الإصدار المتخصص مرتبطًا بـ B
(والذي يتطلب A
).
torkleyy ليس حاليًا ولكن يمكنك القيام بذلك سرًا عن طريق إنشاء سمة يتم تنفيذها لكل من T: Read
و T: BufRead
وتحتوي على أجزاء من الكود الذي تريد التخصص فيه في الضمانات لهذه السمة. لا تحتاج حتى إلى أن تكون مرئية في واجهة برمجة التطبيقات العامة.
فيما يتعلق بمسألة التوافق العكسي ، أعتقد أنه بفضل القواعد اليتيمة يمكننا التخلص من هذه القواعد:
_Am الضمني متوافق مع الإصدارات السابقة للإضافة إلا إذا : _
هذا هو ، أعتقد في جميع الأمثلة الإشكالية أن الضمانة المضافة هي ضمنية شاملة. أردنا أن نقول إن الضمانات الشاملة الافتراضية تمامًا جيدة أيضًا ، ولكن أعتقد أنه يتعين علينا فقط أن نقول أن إضافة الضمانات الشاملة الحالية يمكن أن يكون تغييرًا جذريًا.
السؤال هو ما هو الضمان الذي نريد تقديمه في مواجهة ذلك - على سبيل المثال ، أعتقد أنه سيكون خاصية لطيفة للغاية إذا كان على الأقل يمكن أن يكون الضمانة الشاملة بمثابة تغيير فاصل بناءً على الكود الموجود في الصندوق الخاص بك ، لذلك يمكنك المراجعة صندوقك وتعرف على وجه اليقين ما إذا كنت بحاجة إلى زيادة الإصدار الرئيسي أم لا.
تضمين التغريدة
فيما يتعلق بمسألة التوافق العكسي ، أعتقد أنه بفضل القواعد اليتيمة يمكننا التخلص من هذه القواعد:
_Am الضمني متوافق مع الإصدارات السابقة للإضافة إلا إذا : _
- _ السمة التي يتم تضمينها هي سمة تلقائية ._
- _المتلقي هو معلمة نوع ، وكل سمة في الضمانة كانت موجودة مسبقًا.
هذا هو ، أعتقد في جميع الأمثلة الإشكالية أن الضمانة المضافة هي ضمنية شاملة. أردنا أن نقول إن الضمانات الشاملة الافتراضية تمامًا جيدة أيضًا ، ولكن أعتقد أنه يتعين علينا فقط أن نقول أن إضافة الضمانات الشاملة الحالية يمكن أن يكون تغييرًا جذريًا.
بعد أسبوع والعديد من المناقشات ، تبين للأسف أن هذا ليس هو الحال .
النتائج التي توصلنا إليها هي: crying_cat_face: ولكن أعتقد أن ما كتبته هناك هو نفس استنتاجك. إضافة الضمانات الشاملة هو تغيير جذري ، بغض النظر عن السبب. لكن الضمانات الشاملة فقط (والسمات التلقائية) ؛ على حد علمي ، لم نعثر على حالة يمكن فيها للتضمين غير الشامل أن يكسر رمز المصب (وسيكون ذلك سيئًا للغاية).
لقد اعتقدت في مرحلة ما أننا قد نكون قادرين على تخفيف القواعد اليتيمة حتى تتمكن من تنفيذ سمات لأنواع مثل Vec<MyType>
، ولكن إذا فعلنا ذلك ، فسيتم تنفيذ هذا الموقف بالطريقة نفسها تمامًا هناك:
//crate A
trait Foo { }
// new impl
// impl<T> Foo for Vec<T> { }
// crate B
extern crate A;
use A::Foo;
trait Bar {
type Assoc;
}
// Sadly, this impl is not an orphan
impl<T> Bar for Vec<T> where Vec<T>: Foo {
type Assoc = ();
}
// crate C
struct Baz;
// Therefore, this impl must remain an orphan
impl Bar for Vec<Baz> {
type Assoc = bool;
}
withoutboats آه، فهمت لديك قائمة اثنين من الرصاص كما أو بدلا من و، والتي على ما يبدو هو ما كنت تعني؟
aturon نعم ، قصدت "أو" - هاتان هما الحالتان اللتان يمثل فيهما https://is.gd/k4Xtlp
هذا ما لم يكن يحتوي على أسماء جديدة. AFAIK هو ضمني يحتوي على اسم جديد لا ينكسر أبدًا.
withoutboats أتساءل عما إذا كان بإمكاننا / يجب علينا تقييد الأشخاص الذين يعتمدون على المنطق السلبي حول السمات التلقائية. بمعنى ، إذا قلنا أن إضافة أدوات جديدة للسمات التلقائية هي تغيير قانوني ، فقد نحذر بعد ذلك من الضمانات التي يمكن كسرها بواسطة صندوق المنبع الذي يضيف Send
. سيعمل هذا بشكل أفضل إذا كان لدينا:
default
في الأماكن الإستراتيجية (معظم الوقت) ؛Rc
نيتها ألا تكون Send
- ولكن بعد ذلك لدينا تلك الخاصة بالسمات التلقائية ، لذلك يمكننا أخذها في الاعتبار.لا أعلم أعتقد أن الأمر يعتمد على ما إذا كان هناك دافع قوي أم لا. يبدو من غير المحتمل بشكل خاص أن تدرك أن نوعًا ما يمكن أن يكون له unsafe impl Send/Sync
بعد أن قمت بتحريره بالفعل أعتقد أن هذا سيكون آمنًا في معظم الأوقات ، ستكتب نوعًا بمعرفة مسبقة أنه سيكون آمنًا (لأن هذا هو الهدف من النوع).
أقوم بإضافة unsafe impl Send/Sync
بعد الحقيقة طوال الوقت. أحيانًا لأنني أجعله آمنًا ، أحيانًا لأنني أدرك أن واجهة برمجة تطبيقات C التي أتفاعل معها جيدة لمشاركتها عبر سلاسل الرسائل ، وأحيانًا يكون ذلك بسبب ما إذا كان يجب أن يكون الشيء Send
/ Sync
isn ما أفكر فيه عندما أقدم نوعًا ما.
أقوم بإضافتها بعد الحقيقة أيضًا عند ربط واجهات برمجة تطبيقات C - غالبًا لأن شخصًا ما يسأل صراحةً عن تلك الحدود لذلك أذهب بعد ذلك وأتحقق مما تضمنه المكتبة الأساسية.
شيء واحد لا أحبه في كيفية عمل السمات المرتبطة المتخصصة في الوقت الحالي ، هذا النمط لا يعمل:
trait Buffer: Read {
type Buffered: BufRead;
fn buffer(self) -> impl BufRead;
}
impl<T: Read> Buffer for T {
default type Buffered = BufReader<T>;
default fn buffer(self) -> BufReader<T> {
BufReader::new(self)
}
}
impl<T: BufRead> Buffer for T {
type Buffered = Self;
fn buffer(self) -> T {
self
}
}
هذا لأن النظام الحالي يتطلب أن يكون هذا الضمني صالحًا:
impl Buffer for SomeRead {
type Buffered = SomeBufRead;
// no overriding of fn buffer, it no longer returns Self::Buffered
}
ستطلق السمات impl Trait
الكثير من الرغبة في هذا النوع من النمط ، لكنني أتساءل عما إذا كان هناك حل أفضل حيث يكون الضمني العام صالحًا ولكن هذا التخصص لا يعمل لأنه يقدم خطأ في النوع ؟
withoutboats نعم ، هذا أحد الأسئلة الرئيسية التي لم يتم حلها بشأن التصميم (والتي نسيت طرحها في المناقشات الأخيرة). هناك قدر لا بأس به من النقاش حول هذا الموضوع في سلسلة RFC الأصلية ، لكنني سأحاول كتابة ملخص للخيارات / المقايضات قريبًا.
aturon هل الحل الحالي هو الأكثر تحفظًا (متوافق مع ما نريد القيام به) أم أنه قرار يتعين علينا اتخاذه قبل الاستقرار؟
أنا شخصيًا أعتقد أن الحل الحقيقي الوحيد لهذه المشكلة الذي أثارته withoutboats هو السماح "بتجميع" العناصر معًا عند تحديد علامة default
. إنه نوع من الحل الأفضل هو الأفضل ، لكنني أشعر أن البديل الأسوأ هو الأفضل (تجاوز أي وسيلة تتجاوز الكل) أسوأ قليلاً. (لكن في الواقع withoutboats بالطريقة التي كتبت بها هذا الرمز محيرة. أعتقد أنه بدلاً من استخدام impl BufRead
كنوع إرجاع Buffer
، كنت تقصد Self::BufReader
، أليس كذلك؟)
في هذه الحالة ، يُسمح بما يلي:
trait Buffer: Read {
type Buffered: BufRead;
fn buffer(self) -> impl BufRead;
}
impl<T: Read> Buffer for T {
default {
type Buffered = BufReader<T>;
fn buffer(self) -> BufReader<T> {
BufReader::new(self)
}
}
}
impl<T: BufRead> Buffer for T {
type Buffered = Self;
fn buffer(self) -> T {
self
}
}
لكن ربما يمكننا استنتاج هذه التجمعات؟ لم أفكر كثيرًا في الأمر ، ولكن يبدو أن حقيقة أن العناصر الافتراضية "متشابكة" ظاهرة من تعريف السمة.
لكن في الواقع withoutboats ، الطريقة التي كتبت بها هذا الرمز محيرة. أعتقد أنه بدلاً من استخدام BufRead كنوع إرجاع من Buffer ، كنت تقصد Self :: BufReader ، أليس كذلك؟
نعم ، لقد قمت بتعديل الحل إلى أحد السمات الضمنية ثم عدت للخلف ولكني فقدت نوع الإرجاع في السمة.
ربما يكون شيئًا مثل نظام الكتابة لهذه اللغة مثيرًا للاهتمام أيضًا ، لأنه يبدو مشابهًا لـ Rusts ، لكن مع بعض الميزات ، قد تحل المشكلات الحالية.
( A <: B
سيكون في Rust صحيحًا عندما يكون A
عبارة عن بنية وتنفذ السمة B
، أو عندما يكون A
سمة ، والتطبيقات العامة لكائنات هذه السمة موجودة ، على ما أعتقد)
يبدو أن هناك مشكلة في سمة Display
للتخصص.
على سبيل المثال ، هذا المثال لا يجمع:
use std::fmt::Display;
pub trait Print {
fn print(&self);
}
impl<T: Display> Print for T {
default fn print(&self) {
println!("Value: {}", self);
}
}
impl Print for () {
fn print(&self) {
println!("No value");
}
}
fn main() {
"Hello, world!".print();
().print();
}
مع الخطأ التالي:
error[E0119]: conflicting implementations of trait `Print` for type `()`:
--> src/main.rs:41:1
|
35 | impl<T: Display> Print for T {
| _- starting here...
36 | | default fn print(&self) {
37 | | println!("Value: {}", self);
38 | | }
39 | | }
| |_- ...ending here: first implementation here
40 |
41 | impl Print for () {
| _^ starting here...
42 | | fn print(&self) {
43 | | println!("No value");
44 | | }
45 | | }
| |_^ ...ending here: conflicting implementation for `()`
بينما هذا يجمع:
pub trait Print {
fn print(&self);
}
impl<T: Default> Print for T {
default fn print(&self) {
}
}
impl Print for () {
fn print(&self) {
println!("No value");
}
}
fn main() {
"Hello, world!".print();
().print();
}
شكرا لإصلاح هذه المشكلة.
antoyo ، هل أنت متأكد من أن هذا هو السبب في أن Display
خاص ، أم أن السبب Display
لم يتم تطبيقه على tuples بينما Default
هو؟
تضمين التغريدة
لا أعرف ما إذا كان السعر يقارب Display
، لكن ما يلي يعمل مع سمة Custom
لم يتم تنفيذها لـ tuples:
pub trait Custom { }
impl<'a> Custom for &'a str { }
pub trait Print {
fn print(&self);
}
impl<T: Custom> Print for T {
default fn print(&self) {
}
}
impl Print for () {
fn print(&self) {
println!("No value");
}
}
fn main() {
"Hello, world!".print();
().print();
}
بالمناسبة ، هذا هو الشيء الحقيقي الذي أريد تحقيقه بالتخصص:
pub trait Emit<C, R> {
fn emit(callback: C, value: Self) -> R;
}
impl<C: Fn(Self) -> R, R, T> Emit<C, R> for T {
default fn emit(callback: C, value: Self) -> R {
callback(value)
}
}
impl<C> Emit<C, C> for () {
fn emit(callback: C, _value: Self) -> C {
callback
}
}
أريد استدعاء دالة افتراضيًا ، أو إرجاع قيمة إذا كانت المعلمة ستكون وحدة.
أحصل على نفس الخطأ حول عمليات التنفيذ المتضاربة.
من الممكن (أو هل هذا ممكن) أن تفعل ذلك مع التخصص؟
إذا لم يكن كذلك، ما هي البدائل؟
تحرير: أعتقد أنني اكتشفت سبب عدم تجميعها:
T
في for T
أكثر عمومية من ()
في for ()
لذلك لا يمكن أن يكون أول impl
هو التخصص.
و C
أكثر عمومية من C: Fn(Self) -> R
لذا لا يمكن أن يكون impl
هو التخصص.
من فضلك قل لي إذا كنت مخطئا.
لكن ما زلت لا أفهم لماذا لا يعمل مع المثال الأول مع Display
.
هذا هو السلوك الصحيح حاليًا.
في المثال Custom
، لا تتداخل تلك الضمانات بسبب التفكير السلبي المحلي الخاص. نظرًا لأن السمة مأخوذة من هذا الصندوق ، يمكننا أن نستنتج أن ()
، الذي لا يحتوي على صفة Custom
، لا يتداخل مع T: Custom
. لا تخصص ضروري.
ومع ذلك ، فإننا لا نقوم بهذا المنطق السلبي للصفات التي ليست من صندوقك. يمكن أن تضيف المكتبة القياسية Display for ()
في الإصدار التالي ، ولا نريد أن يكون هذا تغييرًا جذريًا. نريد أن تتمتع المكتبات بحرية إجراء تلك الأنواع من التغييرات. لذلك على الرغم من أن () لا يتضمن العرض ، لا يمكننا استخدام هذه المعلومات في فحص التداخل.
ولكن أيضًا ، نظرًا لأن () لا يتضمن العرض ، فهو ليس أكثر تحديدًا من T: Display
. هذا هو السبب في أن التخصص لا يعمل ، بينما في الحالة الافتراضية ، (): Default
، لذلك فإن ذلك الضمني أكثر تحديدًا من T: Default
.
الضمانات مثل هذه هي نوع من "النسيان" حيث لا يمكننا افتراض أنها تتداخل أو لا تتداخل. نحن نحاول اكتشاف طريقة مبدئية لإنجاح هذا الأمر ، لكنه ليس أول تطبيق للتخصص ، إنه امتداد متوافق مع الإصدارات السابقة لهذه الميزة يأتي لاحقًا.
لقد قدمت # 40582 لتتبع مشكلة السلامة المتعلقة بالحياة.
واجهت مشكلة في محاولة استخدام التخصص ، لا أعتقد أنه مماثل تمامًا لما كان لدى بتقديمه كمسألة منفصلة # 41140 ، يمكنني إحضار رمز المثال من ذلك هنا إذا لزم الأمر
@ afonso360 لا ، هناك مشكلة منفصلة.
كنقطة عامة: في هذه المرحلة ، يتم حظر المزيد من العمل على التخصص في العمل على Chalk ، والذي من شأنه أن يسمح لنا بمعالجة مشكلات السلامة ومن المرجح أيضًا أن يوضح ICEs التي يتم ضربها اليوم.
هل يمكن لأحد أن يوضح ما إذا كان هذا خطأ أم شيء ممنوع عمداً؟ https://is.gd/pBvefi
sgrif أعتقد أن المشكلة هنا هي أن إسقاط الأنواع المرتبطة بالتقصير غير مسموح به. يمكن أن تكون التشخيصات أفضل على الرغم من: https://github.com/rust-lang/rust/issues/33481
هل يمكنك توضيح سبب عدم السماح به؟ نحن نعلم أنه لا يمكن إضافة المزيد من الضمانات المحددة ، لأنها تنتهك القواعد اليتيمية.
يشير هذا التعليق إلى أنه ضروري في بعض الحالات من أجل طلب السلامة (على الرغم من أنني لا أعرف السبب) وفي حالات أخرى لإجبار مستهلكي الواجهة على التعامل معها كنوع مجرد: https://github.com/rust- lang / rust / blob / e5e664f / src / librustc / traits / project.rs # L41
هل تمكّن أي شخص من الاطلاع على https://github.com/rust-lang/rust/issues/31844#issuecomment -266221638؟ يجب أن تكون هذه الضمانات صالحة مع التخصص بقدر ما أستطيع أن أقول. أعتقد أن هناك خطأ يمنعهم.
sgrif أعتقد أن مشكلة الكود الخاص بك قد تكون مشابهة للمشكلة في https://github.com/rust-lang/rust/issues/31844#issuecomment -284235369 والتي أوضحهاwithoutboats في https://github.com / rust-lang / rust / issue / 31844 # issuecomment -284268302. ومع ذلك ، بناءً على تعليق withoutboats ، يبدو أن المنطق المحلي الحالي يجب أن يسمح
جانبا ، حاولت تنفيذ ما يلي ولكن دون جدوى:
trait Optional<T> {
fn into_option(self) -> Option<T>;
}
impl<R, T: Into<R>> Optional<R> for T {
default fn into_option(self) -> Option<R> {
Some(self.into())
}
}
impl<R> Optional<R> for Option<R> {
fn into_option(self) -> Option<R> {
self
}
}
توقعت حدسيًا أن يكون Option<R>
أكثر تحديدًا من <R, T: Into<R>> T
، لكن بالطبع ، لا شيء يمنع impl<R> Into<R> for Option<R>
في المستقبل.
لست متأكدًا من سبب عدم السماح بذلك. حتى إذا تمت إضافة impl<R> Into<R> for Option<R>
في المستقبل ، ما زلت أتوقع أن يختار Rust التنفيذ غير default
، بقدر ما أستطيع أن أرى ، السماح لهذا الرمز ليس له أي تأثير على إعادة التوجيه- التوافق.
إجمالاً ، أجد أن العمل مع التخصص أمر محبط للغاية. فقط كل ما أتوقع أن يعمل ليس كذلك. الحالات الوحيدة التي نجحت فيها في التخصص هي تلك التي تكون بسيطة جدًا ، مثل الحصول على impl
s تتضمن T where T: A
و T where T: A + B
. أجد صعوبة في تشغيل أشياء أخرى ، ولا تشير رسائل الخطأ إلى سبب عدم نجاح محاولات التخصص. بالطبع ، لا يزال هناك طريق أمامنا ، لذلك لا أتوقع رسائل خطأ مفيدة للغاية. ولكن يبدو أن هناك عددًا غير قليل من الحالات التي أتوقع فيها حقًا أن يعمل شيء ما (مثل أعلاه) ولكنه لا يعمل ، ومن الصعب جدًا حاليًا التأكد مما إذا كان ذلك لأنني أساء فهم ما هو مسموح به (والأهم من ذلك ، لماذا) ، إذا كان هناك شيء خاطئ ، أو إذا لم يتم تنفيذ شيء ما بعد. سيكون من المفيد جدًا إلقاء نظرة عامة لطيفة على ما يحدث مع هذه الميزة كما هي.
لست متأكدًا من أن هذا في المكان المناسب ، لكننا واجهتنا مشكلة في منتدى المستخدمين أود أن أذكرها هنا.
لا يتم تجميع الكود التالي (المقتبس من RFC هنا ) ليلاً:
#![feature(specialization)]
trait Example {
type Output;
fn generate(self) -> Self::Output;
}
default impl<T> Example for T {
type Output = Box<T>;
fn generate(self) -> Self::Output { Box::new(self) }
}
impl Example for bool {
type Output = bool;
fn generate(self) -> Self::Output { self }
}
لا يبدو هذا الأمر وكأنه خلل ولكنه يشبه إلى حد كبير مشكلة قابلية الاستخدام - إذا تخصص impl
الافتراضي فقط النوع المرتبط في المثال أعلاه ، فإن defaulti impl
من generate
لن ر نوع التحقق.
رابط إلى الموضوع هنا
@ burns47 هناك حل بديل مربك ولكنه مفيد هنا: https://github.com/rust-lang/rust/issues/31844#issuecomment -263175793.
dtolnay ليس مرضيًا تمامًا - ماذا لو كنا متخصصين في السمات التي لا
هل يمكن لأي شخص التعليق على ما إذا تم رفض الكود في العدد التالي عمداً؟ https://github.com/rust-lang/rust/issues/45542
هل يسمح التخصص بإضافة شيء مثل التالي إلى libcore؟
impl<T: Ord> Eq for T {}
impl<T: Ord> PartialEq for T {
default fn eq(&self, other: &Self) -> bool {
self.cmp(other) == Ordering::Equal
}
}
impl<T: Ord> PartialOrd for T {
default fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
بهذه الطريقة يمكنك تنفيذ Ord
لنوعك المخصص والحصول على Eq
و PartialEq
و PartialOrd
يتم تنفيذها تلقائيًا.
لاحظ أن تنفيذ Ord
واشتقاق PartialEq
أو PartialOrd
أمر خطير ويمكن أن يؤدي إلى أخطاء خفية للغاية! باستخدام هذه الضمانات الافتراضية ، ستكون أقل إغراءً لاشتقاق تلك السمات ، لذلك سيتم تخفيف المشكلة إلى حد ما.
بدلاً من ذلك ، نقوم بتعديل الاشتقاق للاستفادة من التخصص. على سبيل المثال ، كتابة #[derive(PartialOrd)]
أعلى struct Foo(String)
يمكن أن يولد الكود التالي:
impl PartialOrd for Foo {
default fn partial_cmp(&self, other: &Foo) -> Option<Ordering> {
self.0.partial_cmp(&other.0)
}
}
impl PartialOrd for Foo where Foo: Ord {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
بهذه الطريقة يتم استخدام الضمانة الافتراضية إذا لم يتم تنفيذ Ord
. ولكن إذا كان الأمر كذلك ، فإن PartialOrd
يعتمد على Ord
. لسوء الحظ ، لم يتم تجميع هذا: error[E0119]: conflicting implementations of trait `std::cmp::PartialOrd` for type `Foo`
stjepang آمل بالتأكيد أن تضاف البطانيات من هذا القبيل - impl<T:Copy> Clone for T
أيضًا.
أعتقد
impl<T: Ord> PartialEq for T
يجب ان يكون
impl<T, U> PartialEq<U> for T where T : PartialOrd<U>
لأن PartialOrd
يتطلب PartialEq
ويمكنه أيضًا توفيره.
في الوقت الحالي ، لا يمكن للمرء حقًا استخدام الأنواع المرتبطة لتقييد التخصص ، وذلك لأنه لا يمكن تركها غير محددة ولأنها تؤدي إلى تكرار غير ضروري . انظر https://github.com/dhardy/rand/issues/18#issuecomment -358147645
في النهاية ، أحب أن أرى ما أسميه مجموعات التخصص مع بناء الجملة الذي اقترحه nikomatsakis هنا https://github.com/rust-lang/rust/issues/31844#issuecomment -249355377 وبشكل مستقل بواسطتي. أود كتابة RFC حول هذا الاقتراح لاحقًا عندما نكون أقرب إلى استقرار التخصص.
فقط في حالة عدم رؤيتها لأحد ، يغطي منشور المدونة هذا اقتراحًا لجعل التخصص يبدو في مواجهة الإرسال القائم على مدى الحياة.
نظرًا لأن عمليات إغلاق النسخ قد تم تثبيتها بالفعل في الإصدار التجريبي ، أصبح لدى المطورين حافز أكبر لتحقيق الاستقرار في التخصص الآن. والسبب هو أن Fn
و FnOnce + Clone
يمثلان مجموعتين متداخلتين من الإغلاق ، وفي كثير من الحالات نحتاج إلى تطبيق سمات لكليهما.
فقط اكتشف أن صياغة rfc 2132 تشير إلى وجود 5 أنواع فقط من عمليات الإغلاق:
FnOnce
(إغلاق move
مع كون جميع المتغيرات الملتقطة ليست Copy
ولا Clone
)FnOnce + Clone
(إغلاق move
مع كون جميع المتغيرات الملتقطة هي Clone
)FnOnce + Copy + Clone
(إغلاق move
مع كون جميع المتغيرات الملتقطة هي Copy
وهكذا Clone
)FnMut + FnOnce
(إغلاق بخلاف move
مع المتغيرات الملتقطة المتغيرة)Fn + FnMut + FnOnce + Copy + Clone
(إغلاق بخلاف move
بدون المتغيرات الملتقطة المتحولة)لذا إذا لم تكن المواصفات متاحة في المستقبل القريب ، فربما يتعين علينا تحديث تعريفنا لسمات Fn
حتى لا يتداخل Fn
مع FnOnce + Clone
؟
أتفهم أن شخصًا ما قد نفذ بالفعل أنواعًا معينة هي Fn
بدون Copy/Clone
، لكن هل يجب إيقاف هذا؟ أعتقد أن هناك دائمًا طريقة أفضل للقيام بنفس الشيء.
هل من المفترض أن يسمح التخصص بما يلي (لاحظ غياب default
) أم أنه خطأ؟
#![feature(specialization)]
mod ab {
pub trait A {
fn foo_a(&self) { println!("a"); }
}
pub trait B {
fn foo_b(&self) { println!("b"); }
}
impl<T: A> B for T {
fn foo_b(&self) { println!("ab"); }
}
impl<T: B> A for T {
fn foo_a(&self) { println!("ba"); }
}
}
use ab::B;
struct Foo;
impl B for Foo {}
fn main() {
Foo.foo_b();
}
بدون تخصص ، يفشل هذا في البناء باستخدام:
error[E0119]: conflicting implementations of trait `ab::B` for type `Foo`:
--> src/main.rs:24:1
|
11 | impl<T: A> B for T {
| ------------------ first implementation here
...
24 | impl B for Foo {}
| ^^^^^^^^^^^^^^ conflicting implementation for `Foo`
glandium هذا هو https://github.com/rust-lang/rust/issues/48444
فعلا؟ لا يوجد ضمني فارغ في المثال الخاص بي.
تضمين التغريدة
impl B for Foo {}
MoSal ولكن هذا يعني "ليس فارغًا" لأن B
يضيف طريقة بتطبيق افتراضي.
gnzlbg فارغ بالتعريف. لا شيء بين الأقواس.
#![feature(specialization)]
use std::borrow::Borrow;
#[derive(Debug)]
struct Bla {
bla: Vec<Option<i32>>
}
// Why is this a conflict ?
impl From<i32> for Bla {
fn from(i: i32) -> Self {
Bla { bla: vec![Some(i)] }
}
}
impl<B: Borrow<[i32]>> From<B> for Bla {
default fn from(b: B) -> Self {
Bla { bla: b.borrow().iter().map(|&i| Some(i)).collect() }
}
}
fn main() {
let b : Bla = [1, 2, 3].into();
println!("{:?}", b);
}
error[E0119]: conflicting implementations of trait `std::convert::From<i32>` for type `Bla`:
--> src/main.rs:17:1
|
11 | impl From<i32> for Bla {
| ---------------------- first implementation here
...
17 | impl<B: Borrow<[i32]>> From<B> for Bla {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `Bla`
|
= note: upstream crates may add new impl of trait `std::borrow::Borrow<[i32]>` for type `i32` in future versions
ألن يمنع التخصص النزاعات المستقبلية المحتملة؟
يا إلهي ، هذه ميزة بطيئة الحركة! لم يحدث تقدم في أكثر من عامين ، على ما يبدو (بالتأكيد وفقًا للمنشور الأصلي) هل تخلى فريق لانج عن هذا؟
alexreg انظر http://aturon.github.io/2018/04/05/sound-specialization/ للحصول على أحدث التطورات.
alexreg اتضح أن الصلاح _ صعب_. أعتقد أن هناك بعض العمل على فكرة "الضمانات القابلة للتطبيق دائمًا" التي تحدث حاليًا ، لذلك هناك تقدم. انظر https://github.com/rust-lang/rust/pull/49624. أيضًا ، أعتقد أن مجموعة عمل الطباشير تعمل على تنفيذ فكرة "الضمانات القابلة للتطبيق دائمًا" أيضًا ، لكنني لا أعرف إلى أي مدى وصل ذلك.
بعد قليل من الجدل ، يبدو أنه من الممكن التنفيذ الفعال لتضمينات التقاطع بالفعل عبر اختراق باستخدام specialization
و overlapping_marker_traits
.
https://play.rust-lang.org/؟gist=cb7244f41c040db41fc447d491031263&version=nightly&mode=debug
حاولت كتابة دالة متخصصة متكررة لتنفيذ ما يعادل كود C ++ هذا:
كود C ++
#include <cassert>
#include <vector>
template<typename T>
size_t count(T elem)
{
return 1;
}
template<typename T>
size_t count(std::vector<T> vec)
{
size_t n = 0;
for (auto elem : vec)
{
n += count(elem);
}
return n;
}
int main()
{
auto v1 = std::vector{1, 2, 3};
assert(count(v1) == 3);
auto v2 = std::vector{ std::vector{1, 2, 3}, std::vector{4, 5, 6} };
assert(count(v2) == 6);
return 0;
}
حاولت هذا:
كود الصدأ
#![feature(specialization)]
trait Count {
fn count(self) -> usize;
}
default impl<T> Count for T {
fn count(self) -> usize {
1
}
}
impl<T> Count for T
where
T: IntoIterator,
T::Item: Count,
{
fn count(self) -> usize {
let i = self.into_iter();
i.map(|x| x.count()).sum()
}
}
fn main() {
let v = vec![1, 2, 3];
assert_eq!(v.count(), 3);
let v = vec![
vec![1, 2, 3],
vec![4, 5, 6],
];
assert_eq!(v.count(), 6);
}
لكني أحصل على:
overflow evaluating the requirement `{integer}: Count`
لا أعتقد أن هذا يجب أن يحدث لأن impl<T> Count for T where T::Item: Count
يجب ألا يتجاوز.
تحرير: آسف ، لقد رأيت للتو أن هذا قد تم ذكره بالفعل
Boiethios تعمل
#![feature(specialization)]
trait Count {
fn count(self) -> usize;
}
impl<T> Count for T {
default fn count(self) -> usize {
1
}
}
impl<T> Count for T
where
T: IntoIterator,
T::Item: Count,
{
fn count(self) -> usize {
let i = self.into_iter();
i.map(|x| x.count()).sum()
}
}
fn main() {
let v = vec![1, 2, 3];
assert_eq!(v.count(), 3);
let v = vec![vec![1, 2, 3], vec![4, 5, 6]];
assert_eq!(v.count(), 6);
}
هل لم يتم إصلاح فتحة السلامة بعد؟
alexreg لا أعتقد ذلك. انظر http://smallcultfollowing.com/babysteps/blog/2018/02/09/maximally-minimal-specialization-always-applicable-impls/
أعتقد أن الجميع يركزون على الإصدار الآن ...
حسنًا شكرًا ... يبدو أن هذه المشكلة تطول إلى الأبد ، لكنها عادلة بما يكفي. إنها صعبة ، أعرف. ولسوء الحظ ، يتم توجيه الانتباه إلى مكان آخر الآن.
هل يمكن لشخص ما أن يشرح بشكل ملموس الأساس المنطقي وراء عدم السماح بإسقاطات للأنواع المرتبطة الافتراضية في الحالات أحادية الشكل بالكامل؟ لدي حالة استخدام حيث أرغب في هذه الوظيفة (على وجه الخصوص ، سيكون من الخطأ المعنوي أن يتم استدعاء السمة بأنواع لم تكن أحادية الشكل بالكامل) ، وإذا لم تكن هناك مشكلة تتعلق بالسلامة ، فأنا لا أفهم تمامًا سبب هذا غير مسموح به.
pythonesque هناك بعض المناقشات على https://github.com/rust-lang/rust/pull/42411
آه ، أفهم ما إذا اتضح أن الإسقاط يتفاعل بشكل سيء مع التخصص بشكل عام. . ومن الصحيح بالفعل أن ما أريده هو نكهة "التفكير السلبي" (على الرغم من أن السمات المغلقة لن تكون كافية حقًا).
لسوء الحظ ، لست متأكدًا مما إذا كان هناك حقًا أي طريقة لفعل ما أريد بدون هذه الميزة: أرغب في الحصول على نوع مرتبط ينتج عنه "صحيح" عندما يتساوى نوعان تم تمريرهما إلى الداخل لتنفيذ سمة معينة ، و "خطأ" عندما لا تكون كذلك (حيث تؤدي الحالة "False" إلى إجراء بحث عن سمات أكثر تكلفة يمكن أن يقرر ما إذا كانا متساويين "من الناحية اللغوية"). يبدو أن البديل الحقيقي الوحيد (بالنسبة لي) هو القيام دائمًا بالبحث المكلف ؛ وهذا أمر جيد من الناحية النظرية، ولكن يمكن أن يكون أكثر كلفة.
(يمكنني العمل على حل هذا الأمر إذا كان المقصود أن يتم إغلاق السمة ، من خلال تعداد كل زوج محتمل من المُنشئين في الموضع الرئيسي وإخراجهم صحيحًا أو خطأ ؛ ولكن من المفترض أن يكون مفتوحًا للتمديد خارج المستودع ، بحيث ربما لا تعمل ، خاصة وأن التطبيقات في مستودعات مستخدمين مختلفة لن تعرف بالضرورة عن بعضها البعض).
على أي حال ، ربما يكون هذا مجرد مؤشر على أن ما أريد القيام به هو غير مناسب لنظام السمات ويجب أن أتحول إلى آلية أخرى ، مثل وحدات الماكرو: P
ومن الصحيح بالفعل أن ما أريده هو نكهة "التفكير السلبي" (على الرغم من أن السمات المغلقة لن تكون كافية حقًا).
يتطلب أحد البدائل للاستدلال السلبي أن يقوم النوع بتنفيذ سمة واحدة فقط من مجموعة مغلقة من السمات ، بحيث لا يمكن أن تتداخل التطبيقات مع السمات الأخرى في المجموعة (على سبيل المثال ، T
ينفذ واحدًا من { Float | Int | Bool | Ptr }
).
حتى لو كانت هناك طريقة لفرض ذلك في Rust (وهو غير موجود ، AFAIK؟) ، لا أعتقد أن هذا سيحل مشكلتي. أرغب في أن يتمكن المستخدمون في الصناديق المختلفة من تنفيذ عدد تعسفي من الثوابت الجديدة ، والتي يجب أن تقارن بالتساوي مع نفسها فقط وغير متساوية مع كل ثابت آخر محدد ، بما في ذلك تلك غير المعروفة في وقت تعريف الصندوق. لا أرى كيف يمكن لأي مجموعة مغلقة من السمات (أو حتى مجموعة من عائلات السمات) أن تحقق هذا الهدف بمفردها: هذه مشكلة لا يمكن حلها أساسًا دون النظر مباشرة إلى الأنواع. السبب في أنه سيكون قابلاً للتطبيق مع الإسقاطات الافتراضية هو أنه يمكنك افتراضيًا لكل شيء "عدم المقارنة بالتساوي" ثم تنفيذ المساواة بين ثابتك الجديد لنفسه في أي صندوق حددت الثابت فيه ، والذي لن يتعارض مع اليتيم القواعد لأن جميع الأنواع في تنفيذ السمات كانت في نفس الصندوق. إذا كنت أرغب في أي قاعدة من هذا القبيل تقريبًا ولكن المساواة ، فلن ينجح حتى هذا ، لكن المساواة جيدة بما يكفي بالنسبة لي :)
في الوقت الحاضر كل ليلة ، هذا يعمل:
trait Foo {}
trait Bar {}
impl<T: Bar> Foo for T {}
impl Foo for () {}
ولكن حتى مع التخصص والاستخدام ليلا فإن هذا لا:
#![feature(specialization)]
trait Foo<F> {}
trait Bar<F> {}
default impl<F, T: Bar<F>> Foo<F> for T {}
impl<F> Foo<F> for () {}
هل لهذا سبب منطقي أم أنه خطأ؟
rmanoka أليست هذه مجرد قواعد اليتيم العادية؟ في الحالة الأولى ، لا يمكن للصندوق المتلقين للمعلومات أن impl Bar for ()
لذلك يسمح المترجم بذلك ، ولكن في المثال الثاني ، يمكن أن يتعارض صندوق المصب impl Bar<CustomType> for ()
والذي قد يتعارض مع الضمير الافتراضي الخاص بك.
Boscop في هذا السيناريو ، يجب على أي حال تجاوز impl Bar<bool> for () {}
قبل الضمانات الأخرى ، فأنا أتوقع أن يعمل (وفقًا لـ RFC / التوقع). أليس هذا صحيحا؟
بالتعمق أكثر في خطوط المثال المضاد الذي ذكرته ، أدرك (أو أعتقد) أن المثال يفي باختبار "قابل للتطبيق دائمًا" ، وقد يتم العمل عليه.
ربما تعتمد هذه المشكلة على # 45814.
هل توجد أي خطط لدعم حدود السمات على التقصير غير الموجودة في التخصص؟
كمثال يمكن أن يكون هذا مفيدًا جدًا بالنسبة له ، بحيث يمكنك بسهولة تكوين معالجة لأنواع مختلفة من خلال إنشاء بنية عامة مع Inner تعسفي للوظيفة التي لا ينبغي مشاركتها.
#![feature(specialization)]
trait Handler<M> {
fn handle(&self, m:M);
}
struct Inner;
impl Handler<f64> for Inner {
fn handle(&self, m : f64) {
println!("inner got an f64={}", m);
}
}
struct Struct<T>(T);
impl<T:Handler<M>, M:std::fmt::Debug> Handler<M> for Struct<T> {
default fn handle(&self, m : M) {
println!("got something else: {:?}", m);
self.0.handle(m)
}
}
impl<T> Handler<String> for Struct<T> {
fn handle(&self, m : String) {
println!("got a string={}", m);
}
}
impl<T> Handler<u32> for Struct<T> {
fn handle(&self, m : u32) {
println!("got a u32={}", m);
}
}
fn main() {
let s = Struct(Inner);
s.handle("hello".to_string());
s.handle(5.0 as f64);
s.handle(5 as u32);
}
علاوة على ذلك ، في المثال أعلاه ، هناك شيء غريب جربته - بعد إزالة السمة المقيدة على ضمنية Handler الافتراضية (وأيضًا self.0.handle (m)) يتم تجميع الكود بدون مشاكل. ومع ذلك ، عند إزالة تطبيق u32 ، يبدو أنه يكسر خصم السمة الأخرى:
#![feature(specialization)]
trait Handler<M> {
fn handle(&self, m:M);
}
struct Struct<T>(T);
impl<T, M:std::fmt::Debug> Handler<M> for Struct<T> {
default fn handle(&self, m : M) {
println!("got something else: {:?}", m);
}
}
impl<T> Handler<String> for Struct<T> {
fn handle(&self, m : String) {
println!("got a string={}", m);
}
}
// impl<T> Handler<u32> for Struct<T> {
// fn handle(&self, m : u32) {
// println!("got a u32={}", m);
// }
// }
fn main() {
let s = Struct(());
s.handle("hello".to_string());
s.handle(5.0 as f64);
}
على الرغم من عدم وجود رمز يستدعي المعالج لـ u32 ، فإن عدم وجود التخصص يؤدي إلى عدم ترجمة الكود.
تحرير: يبدو أن هذا هو نفس المشكلة الثانية ("ومع ذلك ، عند إزالة التنفيذ لـ u32 ، يبدو أنه يكسر خصم السمة الأخرى") التي ذكرها غلادي مرة أخرى.
مع rustc 1.35.0-nightly (3de010678 2019-04-11) ، يعطي الكود التالي خطأ:
#![feature(specialization)]
trait MyTrait<T> {
fn print(&self, parameter: T);
}
struct Message;
impl<T> MyTrait<T> for Message {
default fn print(&self, parameter: T) {}
}
impl MyTrait<u8> for Message {
fn print(&self, parameter: u8) {}
}
fn main() {
let message = Message;
message.print(1_u16);
}
خطأ:
error[E0308]: mismatched types
--> src/main.rs:20:19
|
18 | message.print(1_u16);
| ^^^^^ expected u8, found u16
ومع ذلك ، يتم تجميع الكود ويعمل عندما أحذف كتلة impl MyTrait<u8>
:
#![feature(specialization)]
trait MyTrait<T> {
fn print(&self, parameter: T);
}
struct Message;
impl<T> MyTrait<T> for Message {
default fn print(&self, parameter: T) {}
}
/*
impl MyTrait<u8> for Message {
fn print(&self, parameter: u8) {}
}
*/
fn main() {
let message = Message;
message.print(1_u16);
}
هل هذا حسب التصميم ، هل هذا لأن التنفيذ غير مكتمل ، أم أن هذا خطأ؟
أيضًا ، أود أن أعرف ما إذا كانت حالة الاستخدام هذه للتخصص (تنفيذ السمات مع معلمات النوع المتداخلة لنوع ملموس واحد بدلاً من تنفيذ نفس السمة للأنواع المتداخلة) سيتم دعمها. قراءة قسم "تحديد قواعد الأسبقية" في RFC 1210 ، أعتقد أنه سيكون مدعومًا ، لكن RFC لا يقدم مثل هذه الأمثلة ولا أعرف ما إذا كنا لا نزال نتبع RFC هذا بدقة.
الإبلاغ عن غرابة:
trait MyTrait {}
impl<E: std::error::Error> MyTrait for E {}
struct Foo {}
impl MyTrait for Foo {} // OK
// But this one is conflicting with error message:
//
// "... note: upstream crates may add new impl of trait `std::error::Error` for type
// std::boxed::Box<(dyn std::error::Error + 'static)>` in future versions"
//
// impl MyTrait for Box<dyn std::error::Error> {}
لماذا يعتبر Box<dyn std::error::Error>
مميزًا (تجنب استخدام كلمة "خاص") في هذه الحالة؟ حتى لو كان يتضمن std::error::Error
في المستقبل ، فإن impl MyTrait for Box<dyn std::error::Error>
لا يزال تخصصًا صالحًا لـ impl<E: std::error::Error> MyTrait for E
، أليس كذلك؟
لا يزال تخصصًا صالحًا
في حالتك ، لا يمكن impl<E: std::error::Error> MyTrait for E
، لأنه لا يحتوي على أي طرق default
.
@ bjorn3 يبدو أنه يجب أن يعمل ، لكنه لا يعمل حتى إذا قمت بإضافة طرق وهمية
في الصندوق bar
pub trait Bar {}
impl<B: Bar> Bar for Box<B> {}
في الصندوق foo
#![feature(specialization)]
use bar::*;
trait Trait {
fn func(&self) {}
}
impl<E: Bar> Trait for E {
default fn func(&self) {}
}
struct Foo;
impl Trait for Foo {} // OK
impl Trait for Box<dyn Bar> {} // Error error[E0119]: conflicting implementations of trait
لاحظ أنه إذا قمت بتغيير الصندوق bar
إلى
pub trait Bar {}
impl<B: ?Sized + Bar> Bar for Box<B> {}
ثم تجميع الصناديق foo
.
@ bjorn3 يبدو أننا لسنا بحاجة إلى طريقة default
لتخصصها ( ملعب ).
KrishnaSannasi لا يمكنني إعادة إنتاج خطأ "التطبيقات المتعارضة" في ساحة اللعب ).
تحديث: أوه ، فهمت. يجب أن تكون السمة Bar
من صندوق المنبع حتى يعمل المثال.
updogliu أنت مثال لا يظهر التخصص لأن Foo
لا يطبق Error
.
هل أبرمج متأخرًا جدًا الليلة ، أم لا ينبغي أن يتسبب ذلك في تجاوز سعة المكدس؟
#![feature(specialization)]
use std::fmt::Debug;
trait Print {
fn print(self);
}
default impl<T> Print for [T; 1] where T: Debug {
fn print(self) {
println!("{:?}", self);
}
}
impl<T> Print for [T; 1] where T: Debug + Clone {
fn print(self) {
println!("{:?}", self.clone());
}
}
fn main() {
let x = [0u8];
x.print();
}
لطالما كانت كتل default impl
ذات الحبيبات الخشنة تفعل أشياء غريبة جدًا بالنسبة لي ، أود أن أقترح تجربة بناء جملة التخصص الدقيق default fn
بدلاً من ذلك.
تحرير: عند التحقق من RFC ، يكون هذا متوقعًا ، لأن default impl
يعني في الواقع _not_ أن جميع العناصر في كتلة impl
هي default
ed أجد تلك الدلالات مفاجأة على أقل تقدير.
@ HadrienG2 لقد استخدمت دائمًا default fn
في هذا المشروع ولكن هذه المرة نسيت الكلمة الرئيسية default
واقترح المترجم إضافتها إلى impl
. لم أشاهد مشكلة تكرار المكدس من قبل ولم يكن متأكدًا مما إذا كان متوقعًا في هذه المرحلة. شكرًا على الاقتراح ، default fn
يعمل بشكل جيد.
بالنظر إلى RFC الأصلي ، يوجد قسم حول تخصص الضمانات الملازمة. هل أعطى شخص ما ذلك حاولت؟
قد لا يعمل النهج المقترح في RFC بشكل مباشر بعد الآن ، على الأقل ، بالنسبة لطرق الثابت المتأصلة:
// This compiles correctly today:
#![feature(specialization)]
use std::marker::PhantomData;
struct Foo<T>(PhantomData<T>);
impl<T> Foo<T> {
default const fn foo() -> Self { Self(PhantomData) }
// ^^should't default here error?
}
// ----
// Adding this fails:
impl<T: Copy> Foo<T> {
const fn foo() -> Self { Self(PhantomData) }
}
يقترح RFC الأصلي رفع الطريقة إلى سمة ، وتنفيذ ذلك للنوع ، وتخصص الضمني. أفترض أنه بالنسبة إلى طرق const fn ، يجب أن تكون هذه الضمانات الخاصة بالسمة للنوع عبارة عن إشارات ثابتة.
لأي شخص صادف هذا ولديه فضول بشأن الوضع - كان هناك تطوران مفاهديان مهمان في عام 2018:
http://smallcultfollowing.com/babysteps/blog/2018/02/09/maximally-minimal-specialization-always-applicable-impls/
http://aturon.github.io/tech/2018/04/05/sound-specialization/
في الآونة الأخيرة ، كتب nikomatsakis الشهر الماضي (على سبيل المثال ، في سياق آخر ؛ أنا غامق) ما يلي:
كانت هناك مشكلة رئيسية واحدة [في التخصص] لم يتم حلها بشكل مرضٍ أبدًا ، وهي مشكلة تتعلق بالسلامة الفنية حول العمر والسمات [...] ثم [هاتان الوظيفتان المرتبطتان أعلاه]. يبدو أن هذه الأفكار قد حلت المشكلة بشكل أساسي ، لكننا كنا مشغولين في هذه الأثناء ولم يكن لدينا وقت للمتابعة.
يبدو الأمر متفائلًا على الرغم من أنه لا يزال هناك عمل يتعين القيام به.
(نشر هذا لأنني وجدت هذا الموضوع قبل بضعة أسابيع ولم يكن لدي أي فكرة عن تقدم العام الماضي ، ثم عثرت مؤخرًا على هذه المنشورات عن طريق الصدفة. هناك تعليقات أعلاه تذكرها ، لكن GitHub تجعل من الصعب بشكل متزايد رؤية أي منها باستثناء الأول و التعليقات القليلة الأخيرة على سلسلة طويلة: البكاء:. قد يكون من المفيد أن يكون هذا التحديث في وصف المشكلة.)
مرحبا جميعا! هل يمكن لشخص أن يخبرني لماذا لا تعمل حالة الاستخدام هذه؟ البق أو السلوكيات المتوقعة؟
كهذا المثال . impl A for i32
ما يرام ، لكن لا يمكن تجميع impl A for ()
في 1.39.0 كل ليلة.
#![feature(specialization)]
trait A {
fn a();
}
default impl <T: ToString> A for T {
fn a() {}
}
impl A for i32 {
fn a() {}
}
impl A for () {
fn a() {}
}
تجميع الرسالة:
error[E0119]: conflicting implementations of trait `A` for type `()`:
--> src/lib.rs:16:1
|
8 | default impl <T: ToString> A for T {
| ---------------------------------- first implementation here
...
16 | impl A for () {
| ^^^^^^^^^^^^^ conflicting implementation for `()`
|
= note: upstream crates may add new impl of trait `std::fmt::Display` for type `()` in future versions
Hexilee ضع default
على الطرق وليس الضمني.
KrishnaSannasi سبيل المثال 2
zserik نعم ، أعرف. لا أعتقد أنه تم تنفيذه بعد ، أو تم إسقاطه. على أي حال لا يعمل الآن.
من الواضح أنه لا يعمل الآن ، لكنني أعتقد أنه يجب أن يعمل.
أنا أطرح هذا السؤال هنا ، لأنني لم ألاحظ أن هذا الموضوع يظهر في أي مكان آخر - هل هناك أي خطط لـ default
حدد وظائف مكتبة قياسية مختلفة ، على غرار الطريقة التي لدينا بها const
-ified وظائف عندما يعتبر القيام بذلك آمنًا؟ السبب الرئيسي الذي أطالب به هو أن عمليات التنفيذ العامة الافتراضية From
و Into
( impl<T, U: From<T>> Into<U> for T
و impl<T> From<T> for T
) تجعل من الصعب كتابة From
عام شامل Into
التنفيذ من core
، وسيكون من الرائع لو تمكنت من تجاوز هذه التحويلات في الصناديق الخاصة بي.
حتى لو سمحنا بالتخصص مقابل From
/ Into
فلن يساعد ذلك في الضمانات العامة بسبب مشكلة الشبكة.
KrishnaSannasi لا أعتقد أن هذا هو الحال. على سبيل المثال ، يجب أن يعمل هذا الرمز إذا كان From
و Into
قابلين للتخصيص ، لكن ليس لأنهم غير:
impl<M: Into<[S; 2]>, S> From<M> for GLVec2<S> {
fn from(to_array: M) -> GLVec2<S> {
unimplemented!()
}
}
impl<M, S> Into<M> for GLVec2<S>
where
[S; 2]: Into<M>,
{
fn into(self) -> M {
unimplemented!()
}
}
pub struct GLVec2<S> {
pub x: S,
pub y: S,
}
ينجح ذلك إذا قمت بتحويل From
و Into
إلى سمة مخصصة لا تحتوي على تلك التطبيقات العامة: https://play.rust-lang.org/؟version=stable&mode=debug&edition= 2018 & gist = cc126b016ff62643946aebc6bab88c98
Osspial حسنًا ، إذا حاولت والمحاكاة باستخدام
سأكرر ، تغيير From/Into
impl ليكون ضمناً افتراضيًا في المكتبة القياسية لن يجعل الضمانات العامة لـ Into
ممكنة. (ولا يؤثر على الضمانات العامة لـ From
)
مرحبًا ، هناك خلل خطير في تنفيذ التخصص الحالي. أصفه بأنه خطأ لأنه حتى لو كان قرار تصميم صريحًا ، فإنه يمنعنا من استخدام واحدة من أقوى ميزات التخصص ، وهي إمكانية إنشاء "أنواع غير شفافة" (هذا ليس اسمًا رسميًا). هذا النمط هو أحد اللبنات الأساسية الأكثر بدائية في اللغات الأخرى التي توفر فئات الكتابة ، مثل Haskell أو Scala.
هذا النمط بسيط - يمكننا تحديد هياكل مثل WithLabel
أو WithID
والتي تضيف بعض الحقول والطرق إلى الهياكل الأساسية ، لذلك على سبيل المثال إذا أنشأنا WithLabel<WithID<MyType>>
فسنكون قادرين للحصول على id
و label
وجميع الحقول / الطرق بـ MyType
أيضًا. لسوء الحظ ، مع التنفيذ الحالي ، هذا غير ممكن.
يوجد أدناه مثال على رمز يوضح استخدام هذا النمط. لا يتم تجميع الكود الذي تم التعليق عليه ، بينما يجب أن يكون هذا النمط مفيدًا حقًا:
#![feature(specialization)]
use std::ops::Deref;
use std::ops::DerefMut;
// =================
// === WithLabel ===
// =================
struct WithLabel<T>(String, T);
pub trait HasLabel {
fn label(&self) -> &String;
}
impl<T> HasLabel for WithLabel<T> {
fn label(&self) -> &String {
&self.0
}
}
// THIS SHOULD COMPILE, BUT GETS REJECTED
// impl<T> HasLabel for T
// where T: Deref, <Self as Deref>::Target : HasLabel {
// default fn label(&self) -> &String {
// self.deref().label()
// }
// }
impl<T> Deref for WithLabel<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.1
}
}
// ==============
// === WithID ===
// ==============
struct WithID<T>(i32, T);
pub trait HasID {
fn id(&self) -> &i32;
}
impl<T> HasID for WithID<T> {
fn id(&self) -> &i32 {
&self.0
}
}
// THIS SHOULD COMPILE, BUT GETS REJECTED
// impl<T> HasID for T
// where T: Deref, <Self as Deref>::Target : HasID {
// default fn id(&self) -> &i32 {
// self.deref().id()
// }
// }
impl<T> Deref for WithID<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.1
}
}
// =============
// === Usage ===
// =============
struct A(i32);
type X = WithLabel<WithID<A>>;
fn test<T: HasID + HasLabel> (t: T) {
println!("{:?}", t.label());
println!("{:?}", t.id());
}
fn main() {
let v1 = WithLabel("label1".to_string(), WithID(0, A(1)));
// test(v1); // THIS IS EXAMPLE USE CASE WHICH DOES NOT COMPILE
}
لكي نجعل السطر test(v1)
يعمل ، نحتاج إلى إضافة هذه السمة يدويًا:
impl<T: HasID> HasID for WithLabel<T> {
fn id(&self) -> &i32 {
self.deref().id()
}
}
بالطبع ، لإكمالها ، نحتاج أيضًا إلى جعل هذه السمة ضمنية:
impl<T: HasLabel> HasLabel for WithID<T> {
fn label(&self) -> &String {
self.deref().label()
}
}
وهذا سيء جدا . لنوعين فقط ، الأمر بسيط. ومع ذلك ، تخيل أن لدينا 10 تعريفات مختلفة للأنواع غير الشفافة ، والتي تضيف حقولًا مختلفة ، مثل WithID
، WithLabel
، WithCallback
، ... سمها ما شئت. مع السلوك الحالي للتخصصات ، سنحتاج إلى تحديد ... أكثر من 1000 تطبيق سمة مختلف! إذا تم قبول الكود الذي تم التعليق عليه ، فسنحتاج فقط إلى 10 تطبيقات للسمات وسيتطلب تنفيذ كل نوع جديد تنفيذًا إضافيًا واحدًا فقط .
لست متأكدًا من كيفية ارتباط شفرتك بالتخصص. لا تزال الوسيطة الخاصة بك (يتم تجميع الكود الأولي الخاص بك ولكن لا يتم ترجمة سطر test(v1);
المعلق بدون الإشارة اليدوية التي قدمتها) إذا تمت إزالة أول سطر #![feature(specialization)]
.
qnighy يجب تجميع الشفرة بعد إلغاء التعليق على الضمانات HasLabel for T
و HasID for T
- إنهم يستخدمون التخصص. في الوقت الحالي ، تم رفضهم (حاول إلغاء التعليق عليهم في الكود الذي قدمته!). هل يعقل لك الآن؟ 🙂
لنفكر في ثلاث حالات WithLabel<WithID<A>>
، WithID<WithLabel<A>>
و WithLabel<WithLabel<A>>
. ثم
WithLabel<WithID<A>>
و WithLabel<WithLabel<A>>
.WithID<WithLabel<A>>
و WithLabel<WithLabel<A>>
.لذلك لا يفي زوج الضمانات بالشرط التالي من RFC :
للتأكد من أن التخصص متماسك ، سنضمن أنه لأي اثنين من التداخلات
I
وJ
التي تتداخل ، لدينا إماI < J
أوJ < I
. أي ، يجب أن يكون المرء حقًا أكثر تحديدًا من الآخر.
وهي مشكلة حقيقية في حالتك أيضًا لأن الضمانة HasLabel
لـ WithLabel<WithLabel<A>>
يمكن تفسيرها بطريقتين.
كيف يمكننا تغطية هذه الحالة تمت مناقشته بالفعل
تعتبر القيود التي تتناولها القاعدة الشبكية ثانوية إلى حد ما بالنسبة للأهداف الرئيسية للتخصص (كما هو موضح في التحفيز) ، وهكذا ، نظرًا لأنه يمكن إضافة قاعدة الشبكة لاحقًا ، فإن RFC تتمسك بقاعدة السلسلة البسيطة في الوقت الحالي.
qnighy ، شكرًا على التفكير في الأمر.
وهي مشكلة حقيقية في حالتك أيضًا لأن علامة HasLabel الخاصة بـ
WithLabel<WithLabel<A>>
يمكن تفسيرها بطريقتين.
هذا صحيح إذا لم نعتبر أن impl<T> HasLabel for WithLabel<T>
أكثر تخصصًا من impl<T> HasLabel for T
لإدخال WithLabel<WithLabel<A>>
. يغطي جزء RFC الذي قمت بلصقه بالفعل ، ومع ذلك ، أعتقد أن هذا يمثل قيدًا خطيرًا وسأطلب إعادة النظر في الدعم لحالة الاستخدام هذه في الإصدار الأول من هذا الامتداد.
في غضون ذلك ، كنت ألعب بـ negative trait impls
لأنهم قد يحلوا النقاط التي غطتها بالفعل. لقد قمت بإنشاء رمز لا يحتوي على المشاكل التي تصفها (ما لم أفقد شيئًا ما) ، ومع ذلك ، فإنه لا يزال غير مترجم. هذه المرة ، لا أفهم من أين جاءت القيود المذكورة في الخطأ ، حيث يجب ألا يكون الحل غامضًا.
الشيء الجيد هو أن كل شيء يتم تجميعه الآن (بما في ذلك التخصصات) ولكن ليس استخدام test(v1)
:
#![feature(specialization)]
#![feature(optin_builtin_traits)]
use std::ops::Deref;
use std::ops::DerefMut;
// =================
// === WithLabel ===
// =================
struct WithLabel<T>(String, T);
auto trait IsNotWithLabel {}
impl<T> !IsNotWithLabel for WithLabel<T> {}
pub trait HasLabel {
fn label(&self) -> &String;
}
impl<T> HasLabel for WithLabel<T> {
fn label(&self) -> &String {
&self.0
}
}
impl<T> HasLabel for T
where T: Deref + IsNotWithLabel, <Self as Deref>::Target : HasLabel {
default fn label(&self) -> &String {
self.deref().label()
}
}
impl<T> Deref for WithLabel<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.1
}
}
// ==============
// === WithID ===
// ==============
struct WithID<T>(i32, T);
pub trait HasID {
fn id(&self) -> &i32;
}
impl<T> HasID for WithID<T> {
fn id(&self) -> &i32 {
&self.0
}
}
auto trait IsNotWithID {}
impl<T> !IsNotWithID for WithID<T> {}
impl<T> HasID for T
where T: Deref + IsNotWithID, <Self as Deref>::Target : HasID {
default fn id(&self) -> &i32 {
self.deref().id()
}
}
impl<T> Deref for WithID<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.1
}
}
// =============
// === Usage ===
// =============
struct A(i32);
type X = WithLabel<WithID<A>>;
fn test<T: HasID + HasLabel> (t: T) {
println!("{:?}", t.label());
println!("{:?}", t.id());
}
fn main() {
let v1 = WithLabel("label1".to_string(), WithID(0, A(1)));
test(v1);
}
في غضون ذلك ، يمكنك استغلال RFC1268 overlapping_marker_traits
للسماح بسمات غير محددة متداخلة ، لكن هذا الاختراق يتطلب ثلاث سمات أخرى (واحدة لتصفح سمات العلامة ، واثنتان لإعادة الحصول على البيانات المحذوفة من خلال التخصص).
qnighy لقد أنشأت مشكلة منفصلة حول هذا الخطأ: https://github.com/rust-lang/rust/issues/66041
حسنًا ، لقد اكتشفت للتو أن auto traits
لن يكون حلاً هنا أبدًا ، كما (وفقًا لـ https://doc.rust-lang.org/nightly/unstable-book/language-features/optin-builtin-traits. html) تنتشر في جميع الحقول في البنية:
السمات التلقائية ، مثل الإرسال أو المزامنة في المكتبة القياسية ، هي سمات علامة يتم تنفيذها تلقائيًا لكل نوع ، ما لم يتم اختيار النوع أو النوع الذي يحتوي عليه صراحةً عبر ضمني سلبي.
تعديل
qnighy لقد أغفلت بطريقة ما أنك قدمت رابطًا إلى الملعب. ❤️ شكرا جزيلا لك على ذلك. إنه يعمل وأنا مندهش من مدى دقة هذا الحل. العجيب أننا قادرون على التعبير عن ذلك حاليًا وأتمنى ألا يختفي هذا الاحتمال في المستقبل!
في مثل هذه الحالة ، يكون overlapping marker traits
هو الاختراق الوحيد الذي يمكننا استخدامه الآن ، لكنني أعتقد أنه سيكون من الجيد السماح في المستقبل بنوع من الحلول الأسهل للتعبير عن أنواع غير شفافة (كما هو موضح في رسالتي السابقة: https : //github.com/rust-lang/rust/issues/31844#issuecomment-549023367).
مثال بسيط للغاية (تبسيط المثال أعلاه ) والذي يفشل:
trait Trait<T> {}
impl<T> Trait<T> for T {}
impl<T> Trait<()> for T {}
لا أعتقد أن هذا يلحق بالمشكلة المحددة بقواعد الشبكة ، ولكن ربما يعتقد أحد الحلول المفرطة في التبسيط أن الأمر كذلك؟
بدون هذا ، يكون التنفيذ الحالي عديم الفائدة لأهدافي. إذا تم السماح بما ورد أعلاه ، فأعتقد أنه سيكون من الممكن أيضًا تنفيذ From
على أنواع المجمعات (على الرغم من أنني لست متأكدًا من Into
).
لأي شخص لا يعرف حتى الآن: هناك هذه الحيلة الرائعة التي
لست متأكدًا مما إذا كان هذا قد تمت معالجته بالفعل ، ولكن يجب إعادة تعريف السمات ذات التطبيقات الافتراضية لطرقها فقط حتى يمكن تمييزها على أنها default
. مثال؛
trait Trait {
fn test(&self) { println!("default implementation"); }
}
impl<T> Trait for T {
// violates DRY principle
default fn test(&self) { println!("default implementation"); }
}
أقترح بناء الجملة التالي لإصلاح هذا (إذا كان يحتاج إلى إصلاح):
impl<T> Trait for T {
// delegates to the already existing default implementation
default fn test(&self);
}
انتقل إلى # 68309
jazzfool يرجى إعادة ونسق لي على هذه المسألة.
هل يوجد منهج لاختبار التخصص؟ على سبيل المثال ، عند كتابة اختبار يتحقق من صحة التخصص ، تحتاج أولاً إلى معرفة ما إذا كان التخصص الذي تحاول اختباره مطبقًا بالفعل بدلاً من التطبيق الافتراضي.
@ the8472 هل تقصد اختبار المترجم ، أم تقصد اختبار التعليمات البرمجية الخاصة بك؟ يمكنك بالتأكيد كتابة اختبارات الوحدة التي تتصرف بشكل مختلف (على سبيل المثال ، استدعاء fn ، ومعرفة ما إذا كنت تحصل على المتغير المتخصص). ربما تقول أن المتغيرين متكافئين ، باستثناء أحدهما من المفترض أن يكون أسرع ، وبالتالي لست متأكدًا من كيفية اختبار الإصدار الذي تحصل عليه؟ في هذه الحالة ، أوافق ، لا أعرف كيف يمكنك اختبار ذلك الآن.
هل يمكن أن أفترض أن بعض السمات الأخرى مع نفس مجموعة الضمانات ، ولكن حيث تتصرف fns بشكل مختلف ، فقط لطمأنة نفسك.
ربما تقول أن المتغيرين متكافئين ، باستثناء أحدهما من المفترض أن يكون أسرع ، وبالتالي لست متأكدًا من كيفية اختبار الإصدار الذي تحصل عليه؟ في هذه الحالة ، أوافق ، لا أعرف كيف يمكنك اختبار ذلك الآن.
يمكنك اختبار ذلك باستخدام ماكرو. أنا صدئ قليلاً مع صدأ ، لكن شيئًا ما على هذا المنوال ...
[#cfg(test)]
static mut SPECIALIZATION_TRIGGERED : bool = false;
[#cfg(test)]
macro_rules! specialization_trigger {
() => { SPECIALIZATION_TRIGGERED = true; };
}
[#cfg(not(test))]
macro_rules! specialization_trigger {
() => {};
}
ثم استخدم specialization_trigger!()
في الضمانة المتخصصة ، وفي الاختبارات استخدم assert!(SPECIALIZATION_TRIGGERED);
[#cfg(test)] static mut SPECIALIZATION_TRIGGERED : bool = false; ...
سترغب في استخدام thread_local! { static VAR: Cell<bool> = Cell::new(false); }
بدلاً من static mut
لأنه بخلاف ذلك يمكن تعيين المتغير في مؤشر ترابط حالة اختبار واحد وقراءته عن طريق الخطأ من سلسلة رسائل أخرى. تذكر أيضًا إعادة تعيين المتغير في بداية كل اختبار ، وإلا ستحصل على true
من الاختبار السابق.
لدي سؤال بخصوص نص RFC ، وآمل أن يكون هذا مكانًا جيدًا لطرحه.
في قسم إعادة الاستخدام ، يتم إعطاء هذا المثال:
trait Add<Rhs=Self> {
type Output;
fn add(self, rhs: Rhs) -> Self::Output;
fn add_assign(&mut self, rhs: Rhs);
}
// the `default` qualifier here means (1) not all items are implied
// and (2) those that are can be further specialized
default impl<T: Clone, Rhs> Add<Rhs> for T {
fn add_assign(&mut self, rhs: Rhs) {
let tmp = self.clone() + rhs;
*self = tmp;
}
}
أتساءل كيف يُفترض أن يتحقق هذا ، نظرًا لأن tmp
لديه نوع Self::Output
ولا يوجد شيء معروف عن هذا النوع المرتبط. لا يبدو أن نص RFC يشرح ذلك ، على الأقل ليس في أي مكان قريب من المكان المذكور في المثال.
هل هناك آلية معينة هنا من المفترض أن تنجح؟
هل يمكن تقييد هذا التقصير where T: Add<Output = T>
؟ أم أن هذه حلقة سببية؟
RalfJung أوافق على أن هذا يبدو خطأ.
لدي سؤال حول الإجراء: ما مدى أهمية هذه المشكلة وما مدى أهمية تجربة الأشخاص لهذه الميزة؟ كما أفهمها ، فإن التنفيذ الحالي غير سليم وغير مكتمل ومن المحتمل أن يتم استبداله تمامًا بالطباشير أو أي شيء آخر. إذا كان هذا صحيحًا ، فهل يجب علينا فقط إلغاء تنفيذ هذه الميزة وغيرها (مثل GATs) حتى يمكن إعادة بنائها بشكل صحيح؟
من فضلك لا تبطل التنفيذ. لا يزال المكسور وغير السليم وغير المكتمل يسمح بالتجريب.
إذا كان هذا صحيحًا ، فهل يجب علينا فقط إلغاء تنفيذ هذه الميزة وغيرها (مثل GATs) حتى يمكن إعادة بنائها بشكل صحيح؟
من فضلك لا ، PyO3 (مكتبة ربط Python) تعتمد حاليًا على التخصص. راجع https://github.com/PyO3/pyo3/issues/210
ألا يعتمد مبلغ معقول من std
على ذلك أيضًا؟ على الرغم من أنني تذكرت رؤية الكثير من التطبيقات الداخلية المتخصصة للأشياء ذات الصلة بالناقلات والسلسلة. لا يعني ذلك أنه يجب أن يمنع إلغاء التنفيذ ، فقط لأنه لن يكون بهذه البساطة إزالة الأقسام ذات الصلة من مدقق النوع.
Lucretiel نعم ، تعتمد العديد من التحسينات المفيدة (خاصة حول التكرارات) على التخصص ، لذلك سيكون من الانحدار التام لتطبيقه.
على سبيل المثال ، FusedIterator
و TrustedLen
غير مجدية بدون تخصص.
تعتمد مكتبة PyO3 (مكتبة ربط Python) حاليًا على التخصص
هذا مخيف ، بسبب الأجزاء "غير السليمة". المكتبة القياسية بها أخطاء حرجة في السلامة بسبب استخدام التخصص الخاطئ. ما مدى تأكدك من عدم وجود نفس الأخطاء؟ حاول استخدام min_specialization
بدلاً من ذلك ، نأمل على الأقل أقل خطورة.
ربما يتلقى specialization
تحذيرًا مشابهًا لـ const_generics
يقول "هذه الميزة غير مكتملة وغير سليمة ومعطلة ، لا تستخدم في الإنتاج ".
تعتمد العديد من التحسينات المفيدة (خاصة حول التكرارات) على التخصص ، لذلك سيكون من الانحدار التام لتنفيذها.
يعتمدون هذه الأيام على min_specialization
(انظر على سبيل المثال https://github.com/rust-lang/rust/pull/71321) ، الذي يحتوي على أكبر فتحات سلامة موصولة.
تضمين التغريدة
أوافق على أن هذا يبدو خطأ.
أي فكرة ما هو الكود المقصود؟ اعتقدت في البداية أن الغرض من default impl
هو تعيين type Output = Self;
، لكن هذا مستحيل في الواقع Output = T
؟
RalfJung هل هناك فرصة min_specialization
؟ أشعر أن استخدام ميزة غير موثقة تمامًا على صندوق أكثر خطورة من تلك التي عرفت (وربما غير معروفة) أخطاء السلامة. كلاهما ليس جيدًا ، لكن الأخير على الأقل ليس مجرد مكونات داخلية للمترجم.
لم أتمكن من العثور على أي إشارة إلى min_specialization
من مشكلة التتبع هذه خارج # 71321 PR - ووفقًا للكتاب غير المستقر ، فهذه هي مشكلة التتبع لهذه الميزة.
لا أعرف الكثير عن هذه الميزة أيضًا ، لقد رأيت للتو إصلاحات libstd السليمة. تم تقديمه في https://github.com/rust-lang/rust/pull/68970 والذي يشرح بعض الأشياء الأخرى حوله.
matthewjasper هل من المنطقي توثيق هذا أكثر قليلاً ومطالبة المستخدمين الليليين بـ feature(specialization)
بالترحيل؟
يبدو أنه يجب أن يكون هناك تحذير على الأقل. يبدو أن هذه الميزة معطلة بشكل صارخ وخطيرة للاستخدام في حالتها الحالية.
أعتقد أن specialization
يمكن أن يصبح مرادفًا لـ min_specialization
، لكن أضف ميزة unsound_specialization
إذا لزم الأمر للمشاريع الحالية ، مثل PyO3 أو أيًا كان. سيوفر أي شخص يستخدم فقط min_specialization
جهدًا كبيرًا ، لكن أي شخص آخر يتلقى رسالة الخطأ ، ويمكنه البحث هنا عن الاسم الجديد.
تضمين التغريدة
أي فكرة ما هو الكود المقصود؟
حسنًا ، في مرحلة ما ، كنا نفكر في وضع يمكن أن تعتمد فيه الإعدادات الافتراضية على بعضها البعض. لذلك أتخيل في تلك المرحلة أن ما يلي كان سينجح:
default impl<T: Clone, Rhs> Add<Rhs> for T {
type Output = T;
fn add_assign(&mut self, rhs: Rhs) {
let tmp = self.clone() + rhs;
*self = tmp;
}
}
كان التحذير هو أنك إذا تجاوزت أي عضو في impl
، فسيتعين عليك تجاوزهم جميعًا . تراجعنا لاحقًا عن هذه الفكرة ، ثم بدأنا في تكرار العديد من التكرارات ، مثل "المجموعات الافتراضية" (والتي ستعمل هنا أيضًا) ، وفي النهاية لم نعتمد أي حل لأننا توصلنا إلى أنه يمكننا الوصول إليها لاحقًا بمجرد أن نتعامل مع المشكلات الأخرى الملحة (نسخة إلى رقم 71420).
من فضلك لا ، PyO3 (مكتبة ربط Python) تعتمد حاليًا على التخصص. راجع PyO3 / pyo3 # 210
عامل صيانة PyO3 هنا - نحن نفضل الابتعاد عن التخصص حتى نتمكن من الوصول إلى الصدأ المستقر. هل من المحتمل أن يستقر min_specialization
قبل إتمام باقي التخصص؟
أعتقد أنه كان هناك بعض النقاش حول محاولة تثبيت min_specialization في اجتماع تصميم lang للتخطيط لإصدار 2021 (إنه على youtube ؛ آسف ، أنا على هاتفي ، أو سأحاول العثور على رابط). لقد نسيت ما قالوه عنها رغم ذلك
أعتقد أنه كان هناك بعض النقاش حول محاولة تثبيت min_specialization في اجتماع تصميم lang للتخطيط لإصدار 2021 (إنه على youtube ؛ آسف ، أنا على هاتفي ، أو سأحاول العثور على رابط). لقد نسيت ما قالوه عنها رغم ذلك
أعتقد أن هذا هو رابط YouTube الصحيح: https://youtu.be/uDbs_1LXqus
(أيضًا على هاتفي)
نعم ، هذا كل شيء. ها هو رابط للمناقشة المحددة: https://youtu.be/uDbs_1LXqus؟t=2073
لقد كنت أستخدم #[min_specialization]
في مكتبة تجريبية كنت أطورها لذا اعتقدت أنني سأشارك خبراتي. الهدف هو استخدام التخصص في أبسط أشكاله: أن يكون لديك بعض الحالات الضيقة ذات التنفيذ الأسرع من الحالة العامة. على وجه الخصوص ، لتشغيل خوارزميات التشفير في الحالة العامة في وقت ثابت ، ولكن إذا تم وضع علامة على جميع المدخلات Public
للحصول على إصدار متخصص يعمل في وقت متغير أسرع (لأنه إذا كانت عامة ، فإننا لا نفعل ذلك تهتم بتسريب المعلومات عنها خلال وقت التنفيذ). بالإضافة إلى ذلك ، تكون بعض الخوارزميات أسرع اعتمادًا على ما إذا كانت نقطة المنحنى البيضاوي طبيعية أم لا. لجعل هذا يعمل نبدأ به
#![feature(rustc_attrs, min_specialization)]
ثم إذا كنت بحاجة إلى إجراء سمة مسند التخصص كما هو موضح في الحد الأدنى من التخصص ، فقم بتمييز إعلان السمة بـ #[rustc_specialization_trait]
.
تم الانتهاء من كل تخصصي في هذا الملف ، وهنا مثال على سمة مسند التخصص.
تعمل الميزة وتفعل ما أحتاجه بالضبط. من الواضح أن هذا يستخدم علامة داخلية من الصدأ وبالتالي فهو عرضة للكسر دون سابق إنذار.
الجزء السلبي الوحيد من التعليقات هو أنني لا أشعر أن الكلمة الرئيسية default
منطقية. ما يعنيه default
الآن بشكل أساسي هو: "هذا ضمني قابل للتخصيص ، لذا فسر ضمنيًا أن تغطية مجموعة فرعية من هذه المجموعة هي تخصص لها وليس ضمنيًا متضاربًا". المشكلة هي أنه يؤدي إلى كود غريب المظهر:
هنا ، يتخصص الوسيط الثاني في الأول ولكنه أيضًا default
. يبدو أن معنى default
مفقود. إذا نظرت إلى بقية الضمانات ، فمن الصعب جدًا معرفة أي من الضمانات المتخصصة. علاوة على ذلك ، عندما قدمت إشارة خاطئة تتداخل مع واحدة موجودة ، كان من الصعب في كثير من الأحيان معرفة أين أخطأت.
يبدو لي أن هذا سيكون أبسط إذا كان كل شيء متخصصًا وعندما تتخصص في شيء ما ، فإنك تعلن بدقة مما يعني أنك متخصص. تحويل المثال في RFC إلى ما كان يدور في خلدي
impl<A, T> Extend<A, T> for Vec<A> where T: IntoIterator<Item=A>
{
// no need for default
fn extend(&mut self, iterable: T) {
...
}
}
// We declare explicitly which impl we are specializing repeating all type bounds etc
specialize impl<A, T> Extend<A, T> for Vec<A> where T: IntoIterator<Item=A>
// And then we declare explicitly how we are making this impl narrower with ‘when’.
// i.e. This impl is like the first except replace all occurances of ‘T’ with ‘&'a [A]’
when<'a> T = &'a [A]
{
fn extend(&mut self, iterable: &'a [A]) {
...
}
}
شكرا على ملاحظاتك.
تعليقي هنا ، على وجه التحديد البند 6 ، يقدم حالة ملموسة في المكتبة القياسية حيث قد يكون من المرغوب فيه أن يكون لديك تخصص لا يمكن تجاوزه جزئيًا: IndexSet
سيحتاج إلى نوع Output
مميز لأن IndexSet
يمكن تنفيذه بدون Index
، لكننا على الأرجح لا نريد السماح لكلا النوعين بالتعايش مع أنواع Output
مختلفة. نظرًا لأن IndexSet
يمكن أن يكون له تطبيق افتراضي من حيث IndexMut
، سيكون من المعقول السماح بتخصص طريقة index_set
دون السماح بتخصص Output
.
أجد صعوبة في مشاهدة مقاطع الفيديو ، لذلك لا يمكنني البحث عن الفيديو المرتبط ، ومع ذلك ، لدي سؤال واحد حول #[min_specialization]
. كما هي ، هناك سمة rustc_unsafe_specialization_marker
لسمات مثل FusedIterator
التي توفر تلميحات تحسين ، بحيث يمكن التخصص فيها. matthewjasper كتب:
هذا غير سليم لكننا نسمح به على المدى القصير لأنه لا يمكن أن يتسبب في استخدامه بعد عمليات تحرير برمز آمن تمامًا بنفس الطريقة التي يمكن بها للتخصص في طرق السمات.
أفترض أن الخطة هي تنفيذ اقتراح aturon وإضافة طريقة تخصص لسمات مثل هذه ( where specialize(T: FusedIterator)
). ولكن في الوقت الحالي ، يبدو أن أي رمز يمكن أن يتخصص في هذه السمات . إذا استقرت كما هي ، يمكن للناس كتابة تخصصات مستقرة تعتمد عليها ، مما يعني أن هذا عدم الصحة سيستقر.
هل يجب أن يقتصر التخصص في هذه السمات أيضًا على المكتبة القياسية؟ هل تستمد المكتبة القياسية فائدة كافية من قدرتها على التخصص فيها؟
إذا استقرت كما هي ، يمكن للناس كتابة تخصصات مستقرة تعتمد عليها ، مما يعني أن هذا عدم الصحة سيستقر.
أفهم أن min_specialization
as-ليس مخصصًا لتحقيق الاستقرار.
أرغب في الحصول على نوع من العلامات على الضمانات المتخصصة. كانت هناك حالات قليلة جدًا من التعليمات البرمجية في Rustc والمكتبة القياسية لا تفعل ما تبدو عليه لأنه لا توجد طريقة لمعرفة أن التخصص يحدث بالفعل:
تخصص غير ضروري Copy
:
https://github.com/rust-lang/rust/pull/72707/files#diff -3afa644e1d09503658d661130df65f59L1955
"تخصص" ليس:
https://github.com/rust-lang/rust/pull/71321/files#diff -da456bd3af6d94a9693e625ff7303113L1589
تنفيذ تم إنشاؤه بواسطة ماكرو ما لم يتم تمرير علامة تتجاوز الضمني الافتراضي:
https://github.com/rust-lang/rust/pull/73851/files؟file-filters٪5B٪5D=#diff -ebb36dd2ac01b28a3fff54a1382527ddR124
matthewjasper ، لا يبدو أن الرابط الأخير يرتبط بأي مقتطف محدد.
لست متأكدًا مما إذا كان هذا هدفًا واضحًا ، ولكن AIUI حقيقة أن الضمانات المتخصصة لم يتم وضع علامة عليها تمنحك طريقة لتجنب كسر التغييرات على الضمانات الشاملة. لا يتعارض default impl<T> Trait for T
مع دلالات المصب - هؤلاء أصبحوا متخصصين للتو.
هل يمكن أن يكون تحذيرًا فقط إذا لم يتم تمييزه؟
كانت هناك حالات قليلة جدًا من التعليمات البرمجية في Rustc والمكتبة القياسية لا تفعل ما تبدو عليه لأنه لا توجد طريقة لمعرفة أن التخصص يحدث بالفعل
تجربتي مع جافا مماثلة (وإن لم تكن مماثلة تمامًا). قد يكون من الصعب معرفة أي فئة فرعية من الفصل تعمل بالفعل ...
نريد بعض العلامات على الضمانات المتخصصة أيضًا ، أيضًا من أجل الوضوح عند القراءة ، أليس كذلك؟
يمكننا وضع العلامات في كلا المكانين ، مما يؤدي بعد ذلك إلى تحسين رسائل الخطأ أو رسائل التحذير لأنهم يعرفون الآن ما إذا كان التخصص مرغوبًا ويمكنهم الإشارة إلى المكان الآخر إذا كان موجودًا.
إذا أضاف صندوق المنبع ضمنيًا ، فبغض النظر عن الترقية ببساطة ، يمكن أن يستخدم صندوق المصب الحيل التي تسمح بالتجميع مقابل الإصدارين الجديد والقديم ، ولست متأكدًا من أن ذلك مفيد.
أعتقد أن الفرق قد يكون أكبر من أن يظهر التغيير. إنه يشير إلى هذا: https://github.com/rust-lang/rust/blob/fb818d4321dee29e1938c002c1ff79b0e7eaadff/src/librustc_span/def_id.rs#L124
رد: بطانية تشير إلى أنهم يكسرون التغييرات على أي حال:
min_specialization
).cuviper في الواقع ، أشعر أنه لا تزال هناك حالات متطرفة حول إضافة ملحقات بطانية جديدة ، حتى مع التخصص. أتذكر أنني كنت أحاول معرفة ما الذي يتطلبه الأمر للسماح لنا بإضافة impl<T: Copy> Clone for T { }
imp لقد كتبت منشور
بغض النظر ، يمكننا جعله تحذيرًا بسيطًا لعدم وجود تعليق توضيحي #[override]
.
ومع ذلك، إذا أننا يمكن أن يكون تعلن المستخدم الذي impls أنها متخصصة (أي فكرة كيف لنا أن نفعل ذلك)، فإنه تبسيط بعض الأشياء. في الوقت الحالي ، يتعين على المترجم أن يستنتج العلاقات بين الضمانات وهذا دائمًا صعب بعض الشيء.
أحد العناصر المعلقة التي يتعين علينا القيام بها في مشروع الطباشير هو محاولة العودة وتوضيح كيفية التعبير عن التخصص هناك.
كانت هناك حالات قليلة جدًا من التعليمات البرمجية في Rustc والمكتبة القياسية لا تفعل ما تبدو عليه لأنه لا توجد طريقة لمعرفة أن التخصص يحدث بالفعل
تجربتي مع جافا مماثلة (وإن لم تكن مماثلة تمامًا). قد يكون من الصعب معرفة أي فئة فرعية من الفصل تعمل بالفعل ...
مرة أخرى في مايو ، اقترحت بديلاً للتخصص في IRLO لا يعتمد فعليًا على الضمانات المتداخلة ، ولكنه يسمح بدلاً من ذلك بتضمين واحد لـ where match
على معلمة النوع الخاص به:
impl<R, T> AddAssign<R> for T {
fn add_assign(&mut self, rhs: R) where match T {
T: AddAssignSpec<R> => self.add_assign(rhs),
T: Add<R> + Copy => *self = *self + rhs,
T: Add<R> + Clone => { let tmp = self.clone() + rhs; *self = tmp; }
}
}
يمكن بعد ذلك استخدام الصناديق المتلقية للمعلومات مثل impl
لتنفيذ "التخصص" ، لأنه حسب الاصطلاح مثل الضمنية للسمة Trait
ستتطابق أولاً مع الأنواع التي تنفذ سمة أخرى TraitSpec
، وأنواع المصب سيكون قادرًا على تنفيذ تلك السمة لتجاوز السلوك العام:
// Crate upstream
pub trait Foo { fn foo(); }
pub trait FooSpec { fn foo(); }
impl<T> Foo for T {
fn foo() where T {
T : FooSpec => T::foo(),
_ => { println!("generic implementation") }
}
}
fn foo<T : Foo>(t: T) {
T::foo()
}
// crate downstream
struct A {}
struct B {}
impl upstream::FooSpec for A {
fn foo() { println!("Specialized"); }
}
fn main() {
upstream::foo(A); // prints "specialized"
upstream::foo(B); // prints "generic"
}
تمنح هذه الصيغة مزيدًا من التحكم في المنبع لاختيار ترتيب الضمانات القابلة للتطبيق ، وبما أن هذا جزء من توقيع السمة / الوظيفة ، فقد يظهر هذا في الوثائق. تمنع المنظمة البحرية الدولية (IMO) "المطاردة الضمنية" لمعرفة الفرع القابل للتطبيق بالفعل ، لأن أمر الحل واضح.
قد يؤدي هذا إلى جعل الأخطاء حول الأعمار وكتابة المساواة أكثر وضوحًا لأن المنبع فقط يمكن أن يفي بها أثناء تنفيذ التخصص (نظرًا لأن المصب ينفذ فقط "سمة التخصص".
تتمثل عيوب هذه الصيغة في أنها مسار مختلف تمامًا عن ذلك الموجود في RFC ، ويتم تنفيذه منذ عام 2016 ، وأن بعض الأشخاص على الأقل في الخيط عبروا عن مخاوفهم من أنه لن يكون معبرًا و / أو بديهيًا مثل الحالي ميزة التخصص (أجد "المطابقة على الأنواع" بديهية جدًا ، لكنني متحيز لأنني أقترح الصياغة).
قد يكون لبناء جملة المطابقة فائدة أخرى (نحوية): إذا كانت في مرحلة ما في المستقبل ممتدة مع حراس المطابقة الذي تم تقييمه بشكل ثابت ، فلن يحتاج المرء إلى القيام بألعاب الجمباز للتعبير عن الحدود المشروطة بتعبيرات const. على سبيل المثال ، يمكن للمرء تطبيق التخصصات بناءً على size_of
أو align_of
أو needs_drop
أو أحجام المصفوفات.
@ dureuill شكرا للمعلومات! هذه بالفعل فكرة مثيرة للاهتمام. أحد المخاوف التي لدي هو أنه لا يحل بالضرورة بعض حالات الاستخدام الأخرى المتوقعة للتخصص ، خاصةً حالة "سلوك التحسين المتزايد" كما وصفها منشور المدونة هذا . لا يزال ، يستحق أن يؤخذ في الاعتبار
@ dureuill الفكرة مثيرة للاهتمام بالفعل وقد يكون لها الكثير من الإمكانات ، لكن البديل ليس دائمًا تبادلًا مكافئًا.
السبب في أنني لا أعتقد ذلك ، هو أنه لم يتم منح المرء الفرصة ليحل محل التنفيذ الأكثر عمومية بشكل كامل. قد تكون مشكلة أخرى أيضًا حقيقة أننا لا نمتلك دعمًا فعليًا لجميع الميزات الموجودة في بنية RFC where
التي يعتمد عليها اقتراحك.
الاقتراح مثير للاهتمام ، لذلك ربما يكون له RFC الخاص به كميزة منفصلة بدلاً من منافس للتخصص لأن كليهما سيكون مفيدًا ولا أرى أي سبب يمنعهما من العيش معًا.
@ the8472nikomatsakis، @ الظلام الفيلق: شكرا لردود الفعل الإيجابية! أحاول الرد على بعض ملاحظاتك في سلسلة IRLO ، لأنني لا أريد أن أكون مزعجًا جدًا بشأن مشكلة التتبع (أنا آسف لكل واحد منكم توقع أخبارًا عن التخصص ووجد للتو ما أشتاكه: مسح :).
يمكنني فتح RFC منفصل إذا تمكنت من كتابة شيء ما قابل للنشر. في غضون ذلك ، أنا منفتح جدًا على التعليقات حول سلسلة IRLO المرتبطة. لقد أضفت مثالاً أطول من منشور مدونة أتورون ، لذا لا تتردد في التعليق على ذلك!
أنا أيضًا أؤيد وجود نوع من العلامات على الضمانات المتخصصة.
يقترب إصدار 2021 ، مما يسمح لنا بحجز كلمات رئيسية أخرى (مثل specialize
). بالنظر إلى تعقيد هذه الميزة وتاريخها ، لا أعتقد أنها ستستقر قبل إصدار إصدار 2021 (لا تتردد في إثبات أنني مخطئ) مما يعني - في رأيي - التلاعب بـ (أ) كلمة (كلمات رئيسية) جديدة ) شيء منطقي.
وإلا ، فإن الكلمة الرئيسية الحالية الوحيدة التي تبدو ... حسنًا .. مناسبة كمحدد ، قد تكون super
؟
ملخص بإعادة استخدام مثال LLFourn من https://github.com/rust-lang/rust/issues/31844#issuecomment -639977601:
super
(محجوز بالفعل ، ولكن قد يُساء تفسيره أيضًا كبديل لـ default
)super impl<A, T> Extend<A, T> for Vec<A> where T: IntoIterator<Item=A>
specialize
specialize impl<A, T> Extend<A, T> for Vec<A> where T: IntoIterator<Item=A>
spec
(اختصار ل specialize
مثل impl
هو implement
) (قلق قائم من قبلssokolow التي أثيرت في https://github.com/rust-lang / rust / issues / 31844 # issuecomment-690980762)spec impl<A, T> Extend<A, T> for Vec<A> where T: IntoIterator<Item=A>
override
(محجوز بالفعل ، شكرًا @ the8472 https://github.com/rust-lang/rust/issues/31844#issuecomment-691042082)override impl<A, T> Extend<A, T> for Vec<A> where T: IntoIterator<Item=A>
أو
spec
(اختصارspecialize
مثلimpl
لـimplement
)
أصبحت "المواصفات" مألوفة أكثر لدى الناس كاختصار لـ "المواصفات" (على سبيل المثال ، "مواصفات HTML 5") لذلك لا أعتقد أنها ستكون اختصارًا جيدًا لـ "التخصص".
override
هي كلمة مفتاحية محجوزة ، أفترض أنها مخصصة للوظائف ، لذلك قد تكون قابلة للاستخدام في كتلة ضمنية
التخصص يعتمد أيضًا على اللغة - باعتباري أستراليًا فهو متخصص بالنسبة لي ، لذا فإن استخدام "المواصفات" يزيل الغموض المحلي.
التخصص يعتمد أيضًا على اللغة - باعتباري أستراليًا فهو متخصص بالنسبة لي ، لذا فإن استخدام "المواصفات" يزيل الغموض المحلي.
هذا يعمل ، باستثناء أن "المواصفات" هي اختصار شائع للمواصفات ، لذلك أعتقد أن استخدام "المواصفات" للإشارة إلى التخصص سيكون مربكًا. حتى لو تم تهجئة الكلمات بشكل مختلف في أستراليا ، فلا يزال بإمكان الجميع فهم الكلمة المقصودة إذا تمت تهجئتها بحرف "z" أو "s".
بصفتي كنديًا ، يجب أن أقول إن التخصص / التخصص ليس هو الكلمة الوحيدة المستخدمة في البرمجة التي تختلف باختلاف اللغة.
هنا ، نستخدم كلمة "color" ، لكنها دائمًا ما تزعجني في المناسبات النادرة عندما تستخدم لغة البرمجة أو المكتبة ذلك بدلاً من "اللون". للأفضل أو للأسوأ ، تعتبر اللغة الإنجليزية الأمريكية معيارًا واقعيًا في تصميم واجهة برمجة التطبيقات ، ومع كلمات مثل اللون / اللون ، فإن اختيار تفضيل تهجئة على الأخرى أمر لا مفر منه دون أن يتم اختراعه حقًا.
نظرًا لمدى القوة التي أتوقع بها أن تعني كلمة "Specification" "المواصفات" ، أعتقد أن هذا موقف آخر حيث يجب علينا فقط اعتبار التهجئة الإنجليزية الأمريكية الخيار الأسوأ.
قد يكون هذا هو الوضع الفعلي ، لكن هذا لا يعني أنه لا بأس من استخدامها. أجد نفسي أقوم باستيراد مثل "استخدام اللون كلون" على سبيل المثال. أنا دائمًا أتعثر على s vs z أيضًا. أعتقد أنه نظرًا لموقف Rust الإيجابي تجاه الشمولية وإمكانية الوصول ، فمن المنطقي اختيار مصطلحات اللغة التي لا تعتمد على اللغة ، حيث تتراكم إحباطات المستخدمين الصغيرة مثل اللون / اللون و s / z.
أوافق من حيث المبدأ. أنا فقط متشكك في أنه في هذه الحالة ، هناك خيار محايد محليًا لا يسبب مشاكل أكثر مما يحل.
بصفتي متحدثًا أصليًا غير اللغة الإنجليزية ، أجد أنه من الممتع نوعًا ما أن يشتكي المتحدثون الأصليون للغة الإنجليزية من زيادة u
باعتبارها عائقًا أمام الشمولية. فقط تخيل كيف سيكون الأمر إذا لم يتم تهجئة كل شيء بشكل غريب بعض الشيء ، ولكن تمت كتابته بلغة مختلفة تمامًا.
ضع بشكل مختلف: كل مصطلح مستخدم في Rust يعتمد على اللغة.
للأفضل أو للأسوأ ، تتم تهجئة الأشياء في Rust باللغة الإنجليزية الأمريكية. بالنسبة للكثيرين هنا ، يعني هذا العمل بلغتهم الثانية أو الثالثة ؛ بالنسبة للآخرين ، فهذا يعني الاضطرار إلى ضبط الإملاء قليلاً. هذا ما يتطلبه الأمر لجعل مجموعة كاملة من الأشخاص يعملون معًا. أعتقد أن فائدة محاولة اختيار الكلمات التي يتم تهجئتها بنفس الطريقة عبر العديد من المتغيرات في اللغة الإنجليزية هامشية مقارنة باختيار مصطلح جيد لا لبس فيه - و spec
غامضة كما هو موضح أعلاه.
استخدم special
ككلمة رئيسية؟
بدلاً من ذلك ، اصنع كلمتين رئيسيتين: specialize
و specialise
واجعلهما متساويين ...
(أو يمكنك أن تتعلم الهجاء غير الأمريكيين بطريقة صحيحة: نحن: 😂)
لا يمكنني التحدث عما تفعله معظم اللغات ، لكن CSS تستخدم تهجئات اللغة الإنجليزية الأمريكية لكل شيء جديد. يبدو أن اللغة الإنجليزية الأمريكية يتم استخدامها بشكل متكرر في البرمجة أيضًا.
@ mark-im لسوء الحظ ، هذا منحدر زلق يؤدي إلى حجج مفادها أن Rust يجب أن يكون لديه مجموعات بديلة من الكلمات الرئيسية في كل لغة رئيسية قد يأتي المتعلمون منها.
كما أنه يعقد اللغة بلا داعٍ للحصول على مرادفات متعددة للكلمات الرئيسية نظرًا لأن الأشخاص والمحللين يستخدمون فقط لفكرة أن المسافة البيضاء يمكن أن تختلف من هذا القبيل.
(ناهيك عن أنه من المحتمل أن يؤدي إلى دفع المرادفات المكافئة في المكتبات ، الأمر الذي قد يتطلب بعد ذلك تصميمًا وتنفيذيًا من نوع rustdoc لمنعها من أن تكون سلبية).
بدلاً من الجدل حول أي لهجة من اللغة الإنجليزية نريد استخدام هذا المعرف ، ربما يمكننا المساومة ووضعها بالعبرية بدلاً من ذلك؟
ssokolow بينما حجة المنحدر الزلق بشكل عام ليست حجة قوية لاستخدامها ، أنا أتفق معك في هذه الحالة. يمكن للمرء أن يجادل بأن اللغات المتعددة جيدة ولكن هناك سببان على الأقل لعدم ذلك:
a
باللغة السلوفاكية هي and
باللغة الإنجليزية)الآن بالعكس ، لماذا يجب تفضيل اللهجات المختلفة للغة الإنجليزية إن لم تكن لغات أخرى؟ لا أرى نقطة. الاتساق (كل شيء باللغة الإنجليزية الأمريكية) يبدو أبسط وأسهل للفهم وأقل عرضة للخطأ.
كل هذا يقال ، سأكون سعيدًا جدًا بـ "هل تقصد XXX؟" نهج رسالة الخطأ. الكلمات المحايدة التي لا تحتوي على مشاكل أخرى جيدة أيضًا.
على الأقل لا أحد يحتاج لمناقشة كرة القدم في الكود. ؛)
يعيش حوالي 70٪ من الناطقين باللغة الإنجليزية في بلدان تستخدم تهجئة أمريكية.
أيضا..
"غالبًا ما يُنظر إلى التهجئة -ize بشكل غير صحيح على أنها أميركية في بريطانيا. وهي مستخدمة منذ القرن الخامس عشر ، تسبق -ise بأكثر من قرن. -ize تأتي مباشرة من اليونانية -ιζειν -izein واللاتينية -izāre ، بينما - ise يأتي عبر French -iser. يوصي قاموس أوكسفورد الإنجليزي (OED) بـ -ize وسرد نموذج -ise كبديل. "
"منشورات مطبعة جامعة أكسفورد (OUP) - مثل معجم هنري واتسون فاولر لاستخدام اللغة الإنجليزية الحديثة ، وقواعد هارت ، ودليل أكسفورد لاستخدام اللغة الإنجليزية - توصي أيضًا بـ -ize. ومع ذلك ، فإن استخدام اللغة الإنجليزية الحديثة لروبرت ألان بوكيت فاولر يعتبر إما أن تكون مقبولة في أي مكان ما عدا الولايات المتحدة ".
المرجع. https://en.wikipedia.org/wiki/American_and_British_English_spelling_differences# -ise، _- ize _ (- isation، _- ization)
يبدو أن الإسبانية والإيطالية لديهما من الألف إلى الياء أو اثنين ، لذا لست متأكدًا من أين يحصل الفرنسيون على -iser ، ربما من الألمانية؟
هل يمكن نقل المزيد من مسارات الدراجات حول كلمات رئيسية محددة وتسمية إلى سلسلة مفاتيح داخلية ؟ أتابع هذه المشكلة للحصول على تحديثات التقدم بشأن الميزة ، وبدأت هذه المناقشة في الحصول على بعض الضوضاء.
التعليق الأكثر فائدة
لقد كنت أستخدم
#[min_specialization]
في مكتبة تجريبية كنت أطورها لذا اعتقدت أنني سأشارك خبراتي. الهدف هو استخدام التخصص في أبسط أشكاله: أن يكون لديك بعض الحالات الضيقة ذات التنفيذ الأسرع من الحالة العامة. على وجه الخصوص ، لتشغيل خوارزميات التشفير في الحالة العامة في وقت ثابت ، ولكن إذا تم وضع علامة على جميع المدخلاتPublic
للحصول على إصدار متخصص يعمل في وقت متغير أسرع (لأنه إذا كانت عامة ، فإننا لا نفعل ذلك تهتم بتسريب المعلومات عنها خلال وقت التنفيذ). بالإضافة إلى ذلك ، تكون بعض الخوارزميات أسرع اعتمادًا على ما إذا كانت نقطة المنحنى البيضاوي طبيعية أم لا. لجعل هذا يعمل نبدأ بهثم إذا كنت بحاجة إلى إجراء سمة مسند التخصص كما هو موضح في الحد الأدنى من التخصص ، فقم بتمييز إعلان السمة بـ
#[rustc_specialization_trait]
.تم الانتهاء من كل تخصصي في هذا الملف ، وهنا مثال على سمة مسند التخصص.
تعمل الميزة وتفعل ما أحتاجه بالضبط. من الواضح أن هذا يستخدم علامة داخلية من الصدأ وبالتالي فهو عرضة للكسر دون سابق إنذار.
الجزء السلبي الوحيد من التعليقات هو أنني لا أشعر أن الكلمة الرئيسية
default
منطقية. ما يعنيهdefault
الآن بشكل أساسي هو: "هذا ضمني قابل للتخصيص ، لذا فسر ضمنيًا أن تغطية مجموعة فرعية من هذه المجموعة هي تخصص لها وليس ضمنيًا متضاربًا". المشكلة هي أنه يؤدي إلى كود غريب المظهر:https://github.com/LLFourn/secp256kfun/blob/6766b60c02c99ca24f816801fe876fed79643c3a/secp256kfun/src/op.rs#L196 -L206
هنا ، يتخصص الوسيط الثاني في الأول ولكنه أيضًا
default
. يبدو أن معنىdefault
مفقود. إذا نظرت إلى بقية الضمانات ، فمن الصعب جدًا معرفة أي من الضمانات المتخصصة. علاوة على ذلك ، عندما قدمت إشارة خاطئة تتداخل مع واحدة موجودة ، كان من الصعب في كثير من الأحيان معرفة أين أخطأت.يبدو لي أن هذا سيكون أبسط إذا كان كل شيء متخصصًا وعندما تتخصص في شيء ما ، فإنك تعلن بدقة مما يعني أنك متخصص. تحويل المثال في RFC إلى ما كان يدور في خلدي