Rust: & الاقتراض الذاتي المتعارض مع نفسه.

تم إنشاؤها على ٣ فبراير ٢٠١٥  ·  20تعليقات  ·  مصدر: rust-lang/rust

لا أعرف لماذا يحدث هذا ولكن rovar و XMPPwocky على IRC اعتقدا أنه خطأ في المترجم.

struct A {
    a: i32
}

impl A {
    fn one(&mut self) -> &i32{
        self.a = 10;
        &self.a
    }
    fn two(&mut self) -> &i32 {
        loop {
            let k = self.one();
            if *k > 10i32 {
                return k;
            }
        }
    }
}

... يعطي الخطأ التالي ...

<anon>:12:21: 12:25 error: cannot borrow `*self` as mutable more than once at a time
<anon>:12             let k = self.one();
                              ^~~~
<anon>:12:21: 12:25 note: previous borrow of `*self` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `*self` until the borrow ends
<anon>:12             let k = self.one();
                              ^~~~
<anon>:17:6: 17:6 note: previous borrow ends here
<anon>:10     fn two(&mut self) -> &i32 {
...
<anon>:17     }
              ^
error: aborting due to previous error
playpen: application terminated with error code 101

ومن المثير للاهتمام ، إذا تم تغيير الطريقة الثانية إلى ...

    fn two(&mut self) -> &i32 {
        loop {
            let k = self.one();
            return k;
        }
    }

هذا سوف يجمع الغرامة.

روضة الأطفال: http://is.gd/mTkfw5

A-NLL A-borrow-checker A-typesystem C-bug NLL-polonius T-compiler

التعليق الأكثر فائدة

أعتقد أنني واجهت مشكلة مماثلة في الإنتاج حيث يبدو أن الاقتراض غير القابل للتغيير يعيش طويلاً ( الحل البديل ).

يجب أن أذكر أيضًا أن رمز starwed لم يعد يجمع في آخر ليلة. اقترح oberien علامة " regression-from-nightly-to-nightly " ، واقترح أيضًا أنه يجب وضع علامة على nikomatsakis في حال كانت هذه مشكلة ملحة :)

ال 20 كومينتر

أعتقد أن هذا هو السلوك الصحيح. لمعرفة السبب ، ضع في اعتبارك الشفرة ذات التعليقات التوضيحية الصريحة مدى الحياة:

struct A {
    a: i32
}

impl A {
    fn one<'a>(&'a mut self) -> &'a i32{
        self.a = 10;
        &self.a
    }
    fn two<'a>(&'a mut self) -> &'a i32 {
        loop {
            let k = self.one();
            if *k > 10i32 {
                return k;
            }
        }
    }
}

يحتاج two إلى إرجاع مرجع بنفس عمر الاقتراض القابل للتغيير الممنوح له. ومع ذلك ، نظرًا لأنه يحصل على ملف. هذا يعني أن k يجب أن يكون له مدى الحياة 'a . ومع ذلك ، بالنسبة للقيمة التي تم إرجاعها one للحصول على مدى الحياة 'a ، يجب أيضًا أن يكون للإدخال في one مدى الحياة 'a . هذا يعني أن Rust مجبر على عدم "إعادة الاقتراض" self ونقله إلى one . نظرًا لأن المراجع القابلة للتغيير خطية ، فلا يمكن نقلها إلا مرة واحدة ، ولكن loop يعني أنه قد يلزم استدعاء one بنفس المرجع القابل للتغيير بشكل متكرر.

يعمل المثال الثاني فقط لأن Rust يمكنه رؤية أن الحلقة ستعمل مرة واحدة فقط ، لذلك سيتم نقل self مرة واحدة فقط.

تبدو هذه مشكلة مستعارة غير معجمية. يمكنك الحصول على سلوك مشابه بدون أي حلقات على الإطلاق:

struct Foo { data: Option<i32> }

fn main() {
    let mut x = Foo{data: Some(1)};

    foo(&mut x);
}

fn foo(x: &mut Foo) -> Option<&mut i32> {
    if let Some(y) = x.data.as_mut() {
        return Some(y);
    }

    println!("{:?}", x.data); 
    None
}
<anon>:14:22: 14:28 error: cannot borrow `x.data` as immutable because it is also borrowed as mutable
<anon>:14     println!("{:?}", x.data); 
                               ^~~~~~
note: in expansion of format_args!
<std macros>:2:43: 2:76 note: expansion site
<std macros>:1:1: 2:78 note: in expansion of println!
<anon>:14:5: 14:30 note: expansion site
<anon>:10:22: 10:28 note: previous borrow of `x.data` occurs here; the mutable borrow prevents subsequent moves, borrows, or modification of `x.data` until the borrow ends
<anon>:10     if let Some(y) = x.data.as_mut() {
                               ^~~~~~
<anon>:16:2: 16:2 note: previous borrow ends here
<anon>:9 fn foo(x: &mut Foo) -> Option<&mut i32> {
...
<anon>:16 }
          ^
error: aborting due to previous error

لا يستطيع Rustc "التعامل" مع عوائد الاقتراض المشروطة.

CCpcwalton

أعتقد أن المشكلة في الواقع هي أنه لا يتم استخدام قابلية التغيير للقيمة المرجعة ولكن قابلية تغيير حجة الوظيفة.

يستدعي الكود التالي دالة متغيرة تقوم بإرجاع مرجع غير قابل للتغيير. بعد ذلك لا يمكنني أخذ مراجع ثابتة.

struct Foo (u8);

impl Foo {
    fn bar(&mut self) -> &u8 {
        self.0 += 1;
        &self.0
    }
}

fn main() {
    let mut x = Foo(42);
    let a = x.bar(); // note: borrow of `x` occurs here
    let b = x.0; // error: cannot use `x.0` because it was mutably borrowed
}

روضة للأطفال

ما زلت أواجه هذه المشكلة في آخر ليلة. يبدو أنه يتعامل مع المرتجعات على أنها ذات عمر حتى نهاية الوظيفة ، حتى لو اجتازت الوظيفة بدون العائد مدقق الاستعارة. يبدو هذا غريبًا نوعًا ما ، مع الأخذ في الاعتبار كيف أن نقطة العودة هي إنهاء الوظيفة مبكرًا.

إذا ألقيت نظرة على فستجتاز مدقق الاستعارة إذا قمت بإزالة الكلمة الأساسية للعودة ، على الرغم من أن الوظيفة الناتجة لا تعمل بشكل صحيح.

يتم حظر هذا في الأعمار غير المعجمية ، والتي يتم حظرها بدورها في MIR. من الناحية المثالية ، سيتم إصلاح هذا بحلول نهاية عام 2016 ، ولكن الإصلاح لن يتم قريبًا.

بعض المناقشات حول العناصر الداخلية: https://internals.rust-lang.org/t/relaxing-the-borrow-checker-for-fn-mut-self-t/3256

مناقشة ميزة الاقتراض غير المعجمي: https://github.com/rust-lang/rfcs/issues/811

لذا فإن قراءة [الخيط الداخلي] تجعلني أشعر أن هذا غير قابل للإصلاح (حتى مع NLL). هل يستطيع أحد أن يؤكد؟ (eddyb؟)

سم مكعب @ استراحة طويلة / طويلة

هذا بالفعل NLL ، وإذا لم أكن مخطئًا ، فسيتم إصلاحه من خلال المقترحات المختلفة التي قدمتها. هذا يتوافق تقريبًا مع "حالة المشكلة رقم 3" من أول مشاركة لي في المدونة . الفكرة الأساسية (من حيث الصياغة من آخر مشاركة مدونة ) لسبب نجاحها هي أن التصنيف الفرعي للاقتراض سيكون مطلوبًا فقط للتمديد حتى نهاية 'a في فرع if الذي يسبب return .

nikomatsakis هل يمكنك من فضلك توضيح المزيد حول كيفية تفاعل NLL مع هذه المشكلة؟ في نموذجي العقلي لمراجع &mut ، يمكن تمريرها إما عن طريق النقل أو إعادة الاستعارة ، والمشكلة في هذه المسألة أن الإرجاع يتطلب وضع النقل ، وإعادة استخدام self بعد ذلك يتطلب إعادة الاقتراض . حسب فهمي ، فإن عمر المرجع الجديد &mut محدود بعمر المتغير الذي يحمل المرجع &mut - في هذه الحالة ، self المتغير ، وبالتالي فهو مقيد بـ جسم الوظيفة ، وبالتالي لا يمكن أن تمتد خارج استدعاء الوظيفة. هل ستقوم NLL بتغيير هذا الحد من عمليات إعادة الاقتراض (أو ربما لا يوجد مثل هذا القيد)؟

كما أنني أتساءل عما إذا كان إصلاح هذه المشكلة أمرًا مرتبطًا بطبيعته بـ NLL أم أنه ربما يكون متعامدًا؟ إذا كان هذا هو الأخير ، فربما يستحق الأمر إصلاحًا قبل NLL؟

أيضًا ، إذا كانت NLL ستصلح هذه المشكلة ، فهل هذا يعني أنه بموجب NLL لن تضطر أبدًا إلى الاختيار يدويًا بين النقل وإعادة الاقتراض؟

هل سيتم إصلاح هذا في أي وقت قريب؟

تضمين التغريدة

هل يمكنك توضيح المزيد حول كيفية تفاعل NLL مع هذه المشكلة؟

الملخص هو أنه ، اليوم ، إذا تم إرجاع قيمة من الوظيفة على أي مسار ، يجب أن يكون القرض صالحًا لبقية الوظيفة على جميع المسارات. بموجب اقتراح NLL الذي كتبته ، تم رفع هذا القيد.

بمزيد من التفاصيل ، إذا أخذت التوقيع المفصل هنا ، يمكنك أن ترى أنه يجب علينا إرجاع مرجع مع مدى الحياة 'a :

fn two<'a>(&'a mut self) -> &'a i32 { .. }

لاحظ الآن أننا نسمي let k = self.one() ثم نعيد هذا k . هذا يعني أن نوع k يجب أن يكون &'a i32 . وهذا بدوره يعني أنه عندما نطلق عليه self.one() ، يجب أن نوفر له اقتراضًا self النوع &'a mut Self . لذلك ، في let k = self.one() نصدر قرضًا self مع مدى الحياة 'a . هذا العمر أكبر من الحلقة (وبالطبع الوظيفة بأكملها) ، لذلك يستمر أثناء تجولنا في الحلقة ، مما يؤدي إلى تقرير الخطأ. بموجب قواعد NLL التي اقترحتها ، فإن عمر k مطلوب فقط أن يمتد إلى 'a إذا تم أخذ if . لذلك ، إذا لم يتم أخذ if ، فيمكن أن ينتهي القرض في وقت سابق.

هل هذا يساعد؟


@ osa1

هل سيتم إصلاح هذا في أي وقت قريب؟

لا يزال هناك بعض الخطوات التي يجب اتخاذها قبل أن نتمكن من القيام بذلك ، ولكن هناك عمل نشط للقيام بإعادة البناء المطلوبة.

تضمين التغريدة
شكرا ، لقد ساعدت إلى حد ما. ولكن هناك شيء واحد صغير لا أفهمه - ما يحدث بالضبط عندما تكتب self في الكود الخاص بك - كيف تعمل إعادة اقتراض self . لقد قرأت التفسير الخاص بك لـ "حالة المشكلة 3" ( get_default ) ، حيث قمت بتضمين الكود داخل المتصل ، ولكن هناك ، قمت بتغيير كل استخدام لـ self في استعارة متغير map مباشرة ، لذا فإن إزالة الصغائر لم تفسح لي الأمر.

ها هو المكان الذي أكون فيه عالقًا: عندما نتصل بـ let k = self.one() ، لا يمكن نقل self (لأنه مطلوب لاحقًا) ، لذلك يعتبر أنه مستعار. في وقت لاحق ، نعيد هذا k مشروط ، لذلك يجب أن يكون لهذا الاقتراض مدى الحياة 'a ، والذي يتجاوز عمر استدعاء الوظيفة. لكن! لقد اقترضنا من self ، والذي يستمر حتى نهاية الوظيفة فقط. يبدو أن هذا القيد يختصر 'a ، لذلك في نموذجي العقلي ، حتى تحت NLL ، يجب أن نحصل على خطأ "لا يعيش طويلاً بما فيه الكفاية".

krdln لقد *self - أي أننا أعدنا استعارة ما تشير إليه الذات. نسمح لك باقتراض *self لمدى الحياة الذي يفوق self نفسه لأن نوع self يشير إلى "قفل" طوال العمر 'a . ومن ثم ، طالما أننا نضمن أنه خلال 'a ، لن يتم استخدام self ، يجب أن تكون النتيجة مرجعًا حصريًا - في هذه الحالة ، بعد إرجاع fn ، تم تفرقع self ، وبالتالي يمكن لا يتم استخدامها ، لذا يكفي فقط ضمان عدم استخدام الذات حتى نهاية fn. (على الأقل أتمنى أن يكون هذا صحيحًا. =)

يتم حظر هذا في الأعمار غير المعجمية ، والتي يتم حظرها بدورها في MIR. من الناحية المثالية ، سيتم إصلاح هذا بحلول نهاية عام 2016 ، ولكن الإصلاح لن يتم قريبًا.

ليس من المستغرب للغاية ، لكنني أكدت أن المثال الأولي يتم تجميعه بالفعل

أعتقد أنني واجهت مشكلة مماثلة في الإنتاج حيث يبدو أن الاقتراض غير القابل للتغيير يعيش طويلاً ( الحل البديل ).

يجب أن أذكر أيضًا أن رمز starwed لم يعد يجمع في آخر ليلة. اقترح oberien علامة " regression-from-nightly-to-nightly " ، واقترح أيضًا أنه يجب وضع علامة على nikomatsakis في حال كانت هذه مشكلة ملحة :)

عثرت على هذه المشكلة في محاولة للقيام بشيء مشابه جدًا (وهو ما لا يسمح به nll الحالي):

fn f(vec: &mut Vec<u8>) -> &u8 {
    if let Some(n) = vec.iter_mut().find(|n| **n == 1) {
        *n = 10;
        n
    } else {
        vec.push(10);
        vec.last().unwrap()
    }
}

fn main() {
    let mut vec = vec![1, 2, 3];
    f(&mut vec);
}
error[E0499]: cannot borrow `*vec` as mutable more than once at a time
 --> src/main.rs:6:9
  |
1 | fn f(vec: &mut Vec<u8>) -> &u8 {
  |           - let's call the lifetime of this reference `'1`
2 |     if let Some(n) = vec.iter_mut().find(|n| **n == 1) {
  |                      --- first mutable borrow occurs here
3 |         *n = 10;
4 |         n
  |         - returning this value requires that `*vec` is borrowed for `'1`
5 |     } else {
6 |         vec.push(10);
  |         ^^^ second mutable borrow occurs here

error[E0502]: cannot borrow `*vec` as immutable because it is also borrowed as mutable
 --> src/main.rs:7:9
  |
1 | fn f(vec: &mut Vec<u8>) -> &u8 {
  |           - let's call the lifetime of this reference `'1`
2 |     if let Some(n) = vec.iter_mut().find(|n| **n == 1) {
  |                      --- mutable borrow occurs here
3 |         *n = 10;
4 |         n
  |         - returning this value requires that `*vec` is borrowed for `'1`
...
7 |         vec.last().unwrap()
  |         ^^^ immutable borrow occurs here

(كان يجب أن نزيل E-needstest عندما أزلنا NLL-fixed-by-NLL)

هل كانت هذه الصفحة مفيدة؟
0 / 5 - 0 التقييمات