মরিচায় সুস্পষ্ট জীবনকাল কেন প্রয়োজন?


199

আমি মরিচা বইয়ের লাইফটাইম অধ্যায়টি পড়ছিলাম , এবং আমি একটি নাম / স্পষ্ট জীবনকাল জন্য এই উদাহরণটি দেখতে পেয়েছি:

struct Foo<'a> {
    x: &'a i32,
}

fn main() {
    let x;                    // -+ x goes into scope
                              //  |
    {                         //  |
        let y = &5;           // ---+ y goes into scope
        let f = Foo { x: y }; // ---+ f goes into scope
        x = &f.x;             //  | | error here
    }                         // ---+ f and y go out of scope
                              //  |
    println!("{}", x);        //  |
}                             // -+ x goes out of scope

এটি আমার কাছে বেশ স্পষ্ট যে সংকলক দ্বারা ত্রুটিটি রোধ করা হ'ল নিযুক্ত রেফারেন্সের ব্যবহারের পরে মুক্তx : অভ্যন্তরীণ ক্ষেত্রটি সম্পন্ন হওয়ার পরে, fএবং তাই &f.xঅবৈধ হয়ে যায়, এবং তাকে নির্ধারিত করা উচিত হয়নি x

আমার সমস্যাটি হ'ল সুস্পষ্ট আজীবন ব্যবহার না করেই সমস্যাটি সহজেই বিশ্লেষণ করা যেতে পারে , উদাহরণস্বরূপ বিস্তৃত সুযোগের ( ) কোনও রেফারেন্সের একটি অবৈধ অ্যাসাইনমেন্ট দিয়ে । 'ax = &f.x;

কোন ক্ষেত্রে মুক্ত-পরে ব্যবহার (বা অন্য কোনও শ্রেণির?) ত্রুটিগুলি রোধ করার জন্য প্রকৃত জীবনকাল প্রয়োজন?



2
এই প্রশ্নের ভবিষ্যতের পাঠকদের জন্য, দয়া করে নোট করুন এটি বইয়ের প্রথম সংস্করণের সাথে লিঙ্ক করেছে এবং এখন একটি দ্বিতীয় সংস্করণ রয়েছে :)
carol10cents

উত্তর:


205

অন্যান্য উত্তরের সমস্তগুলির মূল বক্তব্য রয়েছে ( এফজেএইচ-র একটি দৃ ?় উদাহরণ যেখানে একটি স্পষ্ট জীবনকাল প্রয়োজন ) তবে একটি মূল বিষয় অনুপস্থিত: সংকলক যখন আপনাকে বলবে যে আপনি সেগুলি ভুল পেয়েছেন তখন কেন স্পষ্ট জীবনকাল প্রয়োজন ?

এটি প্রকৃতপক্ষে একই প্রশ্ন যা "যখন সংকলক তাদের অনুমান করতে পারে তখন সুস্পষ্ট ধরণের কেন প্রয়োজন"। একটি কাল্পনিক উদাহরণ:

fn foo() -> _ {  
    ""
}

অবশ্যই, সংকলকটি দেখতে পাবে যে আমি একটি ফিরে আসছি &'static str, তবে প্রোগ্রামারটি কেন এটি টাইপ করতে হবে?

মূল কারণ হ'ল সংকলকটি দেখতে পাচ্ছে যে আপনার কোডটি কী করে, এটি আপনার উদ্দেশ্য কী তা তা জানে না।

কোডগুলি পরিবর্তনের কোডের প্রভাবগুলিতে ফায়ারওয়ালের একটি প্রাকৃতিক সীমানা। যদি আমরা কোড থেকে জীবনকালকে পুরোপুরি পরিদর্শন করার অনুমতি দিই, তবে একটি নির্দোষ চেহারার পরিবর্তন লাইফটাইমকে প্রভাবিত করতে পারে, যার ফলে খুব দূরে কোনও কার্যক্রমে ত্রুটি হতে পারে। এটি কোনও অনুমানের উদাহরণ নয়। আমি এটি যেমন বুঝতে পারি, আপনি শীর্ষ স্তরের ফাংশনগুলির জন্য টাইপ অনুক্রমের উপর নির্ভর করলে হাস্কেলের এই সমস্যা হয়। মরিচা সেই বিশেষ সমস্যাটি কুঁকড়ে ফেলেছিল।

সংকলকটির কার্যকারিতা উপকারিতাও রয়েছে - প্রকার ও জীবনকাল যাচাই করতে কেবল ফাংশনের স্বাক্ষরগুলি পার্স করা দরকার। আরও গুরুত্বপূর্ণ বিষয় এটির প্রোগ্রামারের পক্ষে এটির দক্ষতা বেনিফিট রয়েছে। আমাদের যদি স্পষ্ট জীবনকাল না থাকে তবে এই ফাংশনটি কী করে:

fn foo(a: &u8, b: &u8) -> &u8

উত্সটি পরীক্ষা না করেই বলা অসম্ভব, যা প্রচুর সংখ্যক কোডিং সেরা অনুশীলনের বিরুদ্ধে যায়।

একটি বিস্তৃত সুযোগে একটি রেফারেন্সের একটি অবৈধ কার্যভার অনুমান দ্বারা

সুযোগগুলি হয় জীবনকালের মূলত। আরও পরিষ্কারভাবে বলা যায়, একটি আজীবন 'aএকটি জেনেরিক আজীবন প্যারামিটার যা কল সাইটের উপর ভিত্তি করে সংকলন সময়ে নির্দিষ্ট সুযোগের সাথে বিশেষীকরণ করা যেতে পারে।

[...] ত্রুটিগুলি রোধ করার জন্য কী প্রকৃত জীবনকাল প্রয়োজন?

একেবারেই না. ত্রুটিগুলি রোধ করার জন্য লাইফটাইমগুলি প্রয়োজন, তবে সামান্য স্যানিটি প্রোগ্রামারদের যা আছে তা রক্ষার জন্য সুস্পষ্ট জীবনকাল প্রয়োজন needed


18
@ জকো কল্পনা করুন f x = x + 1যে আপনি অন্য কোনও মডিউলে ব্যবহার করছেন এমন কোনও স্বাক্ষর ছাড়াই আপনার কয়েকটি শীর্ষ স্তরের ফাংশন রয়েছে । পরে আপনি যদি সংজ্ঞাটি এতে পরিবর্তন করেন তবে এর f x = sqrt $ x + 1ধরণটি এর থেকে পরিবর্তিত Num a => a -> aহয় Floating a => a -> a, যা সমস্ত কল সাইটগুলিতে fযেমন ত্রুটির সাথে ডাকা হয় সেখানে টাইপ ত্রুটি ঘটায় Int। কোনও ধরণের স্বাক্ষর থাকা নিশ্চিত করে যে স্থানীয়ভাবে ত্রুটিগুলি ঘটে।
fjh

11
"স্কোপগুলি লাইফটাইম হয়, মূলত। আরও স্পষ্টভাবে বলা যায়, আজীবন 'এ হ'ল জেনেরিক আজীবন প্যারামিটার যা কল সময়ে নির্দিষ্ট সুযোগের সাথে বিশেষীকরণ করা যেতে পারে" " বাহ এটি সত্যিই দুর্দান্ত, আলোকিত করার বিষয়। আমি এটি চাই যদি এটি স্পষ্টভাবে বইটিতে অন্তর্ভুক্ত করা হত।
কোরাজা

2
@ fjh ধন্যবাদ আমি এটি কুঁচকেছি কিনা তা দেখার জন্য - মুল বক্তব্যটি হ'ল টাইপটি স্পষ্টভাবে যোগ করার আগে বর্ণিত থাকলে sqrt $পরিবর্তনের পরে কেবল স্থানীয় ত্রুটি ঘটতে পারে, এবং অন্য জায়গাগুলিতে প্রচুর ত্রুটি হত না (যা আমরা না করলে আরও ভাল হয়) প্রকৃত প্রকারটি পরিবর্তন করতে চান না)?
কোরাজা

5
@ jco ঠিক আছে। কোনও প্রকার উল্লেখ না করার অর্থ আপনি ভুলক্রমে কোনও ফাংশনের ইন্টারফেস পরিবর্তন করতে পারেন। এটি হেস্কেলের সমস্ত শীর্ষ-স্তরের আইটেমকে টীকায়িত করার জন্য উত্সাহিত হওয়ার একটি কারণ encouraged
fjh

5
এছাড়াও যদি কোনও ফাংশন দুটি রেফারেন্স গ্রহণ করে এবং কোনও রেফারেন্স দেয় তবে এটি কখনও কখনও প্রথম রেফারেন্স এবং কখনও কখনও দ্বিতীয়টি ফিরে আসতে পারে। এক্ষেত্রে ফিরে আসা রেফারেন্সটির জন্য আজীবন অনুমান করা অসম্ভব। সুস্পষ্ট জীবনকাল এমন পরিস্থিতি এড়াতে / পরিষ্কার করতে সহায়তা করে।
মাইকেলমোসার

93

আসুন নীচের উদাহরণটি একবার দেখুন।

fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'a u32 {
    x
}

fn main() {
    let x = 12;
    let z: &u32 = {
        let y = 42;
        foo(&x, &y)
    };
}

এখানে, সুস্পষ্ট জীবনকাল গুরুত্বপূর্ণ। এটি সংকলন করে কারণ ফলাফলটির fooপ্রথম আর্গুমেন্ট ( 'a) এর মতো একই জীবনকাল রয়েছে , সুতরাং এটি তার দ্বিতীয় যুক্তিকে বহির্ভূত করতে পারে। এটি স্বাক্ষর করে আজীবন নামগুলি দ্বারা প্রকাশ করা হয় foo। আপনি যদি সংকেতকারীকে কলটিতে যুক্তিগুলি পরিবর্তন করেন fooতবে অভিযোগ করবে যে yদীর্ঘকাল বেঁচে না:

error[E0597]: `y` does not live long enough
  --> src/main.rs:10:5
   |
9  |         foo(&y, &x)
   |              - borrow occurs here
10 |     };
   |     ^ `y` dropped here while still borrowed
11 | }
   | - borrowed value needs to live until here

16

নিম্নলিখিত কাঠামোতে আজীবন টিকা:

struct Foo<'a> {
    x: &'a i32,
}

নির্দিষ্ট করে যে কোনও Fooদৃষ্টান্তের ( xক্ষেত্র) এতে থাকা রেফারেন্সটি প্রকাশ করা উচিত নয় ।

উদাহরণস্বরূপ আপনি মরচে বইয়ে জুড়ে এসেছিল এই কারণে চিত্রিত করে না fএবং yভেরিয়েবল একই সময়ে পরিধি বাইরে যেতে।

এর চেয়ে উত্তম উদাহরণ হ'ল:

fn main() {
    let f : Foo;
    {
        let n = 5;  // variable that is invalid outside this block
        let y = &n;
        f = Foo { x: y };
    };
    println!("{}", f.x);
}

এখন, fসত্যিই দ্বারা চিহ্নিত পর্বে পরিবর্তনশীল আউটলাইভ f.x


9

নোট করুন যে কাঠামোর সংজ্ঞা ব্যতীত কোডের টুকরোটিতে কোনও স্পষ্ট জীবনকাল নেই। সংকলকটি জীবনকালকে সঠিকভাবে অনুমান করতে সক্ষম main()

প্রকারের সংজ্ঞাগুলিতে, তবে, সুস্পষ্ট জীবনকাল অনিবার্য। উদাহরণস্বরূপ, এখানে একটি অস্পষ্টতা রয়েছে:

struct RefPair(&u32, &u32);

এগুলি কি আলাদা লাইফটাইম হওয়া উচিত বা এগুলি একই হওয়া উচিত? এটি ব্যবহারের দৃষ্টিকোণ থেকে গুরুত্বপূর্ণ, এর struct RefPair<'a, 'b>(&'a u32, &'b u32)থেকে খুব আলাদা struct RefPair<'a>(&'a u32, &'a u32)

এখন, সরল মামলার ক্ষেত্রে যেমন আপনি সরবরাহ করেছেন, সংকলকটি তাত্ত্বিকভাবে অন্যান্য জায়গাগুলির মতো অভিজাত জীবনযাপন করতে পারে তবে এই ধরনের ক্ষেত্রে খুব সীমাবদ্ধ এবং সংকলকটিতে অতিরিক্ত জটিলতার পক্ষে মূল্যহীন নয় এবং স্পষ্টতার সাথে এই লাভটি হবে খুব কম প্রশ্নবিদ্ধ।


2
তারা ব্যাখ্যা করতে পারেন কেন তারা খুব আলাদা?
এবি

@ এ বি দ্বিতীয় দ্বিতীয়টির জন্য উভয় উল্লেখ একই আজীবন ভাগ করা প্রয়োজন। এর অর্থ রেফার পেয়ার ১. রিফায়ার ২ এর চেয়ে বেশি বাঁচতে পারে না এবং এর বিপরীতে - সুতরাং উভয় রেফকে একই মালিকের সাথে কোনও কিছুর প্রতি নির্দেশ করা প্রয়োজন। প্রথমদিকে তবে কেবল রেফপায়ারটির উভয় অংশকেই আউটলাইভ করতে হবে।
লোগিক

2
@AB, এটা প্রনয়ন কারণ তাদের উভয়ই জীবনকালের একীভূত হয় - কারণ স্থানীয় জীবনকালের যে ছোট 'static, 'staticসব জায়গায় ব্যবহার করা যেতে পারে যেখানে স্থানীয় জীবনকালের ব্যবহার করা যেতে পারে তাই আপনার উদাহরণে pতার জীবদ্দশায় প্যারামিটারের স্থানীয় জীবদ্দশায় যেমন অনুমিত থাকবে y
ভ্লাদিমির মাত্তেভ

5
@ এ্যাব এর RefPair<'a>(&'a u32, &'a u32)অর্থ হ'ল 'aউভয় ইনপুট লাইফটাইমগুলির ছেদ হবে, অর্থাত্ এই ক্ষেত্রে জীবনকাল y
fjh

1
@ লোগিক "" কি রেফপায়ার এর উভয় অংশকেই ছাড়িয়েছে "? আমি যদিও এটি বিপরীত ছিল ... একটি & u32 এখনও রেফপায়ার ছাড়াই বোধগম্য করতে পারে, অন্যদিকে রেফ-পেয়ারটি রেফ পেয়ারটি অদ্ভুত হবে।
কেইড

6

বই থেকে কেসটি ডিজাইনের মাধ্যমে খুব সহজ। জীবনকালীন বিষয়টিকে জটিল বলে মনে করা হয়।

সংকলক একাধিক যুক্তি সহ কোনও কার্যক্রমে সহজেই আজীবন অনুমান করতে পারে না।

এছাড়াও, আমার নিজস্ব al চ্ছিক ক্রেটটির এমন OptionBoolএকটি as_sliceপদ্ধতি রয়েছে যার স্বাক্ষরটি আসলে:

fn as_slice(&self) -> &'static [bool] { ... }

সংকলকটি যে এটি খুঁজে পেতে পারে তার কোনও উপায় নেই।


আইআইএনএম, একটি দ্বি-আর্গুমেন্ট ফাংশনটির রিটার্ন টাইপের আজীবন অনুমান করা থামানো সমস্যার সমতুল্য হবে - আইওডাব্লু, নির্দিষ্ট সময়ের মধ্যে সীমাবদ্ধ নয়।
dstromberg

4

আমি এখানে আরও দুর্দান্ত ব্যাখ্যা খুঁজে পেয়েছি: http://doc.rust-lang.org/0.12.0/guide- Lifetimes.html# returning-references

সাধারণভাবে, কেবলমাত্র রেফারেন্সগুলি যদি কোনও পরামিতি থেকে প্রক্রিয়াতে নেওয়া হয় তবেই ফিরে আসা সম্ভব। সেক্ষেত্রে পয়েন্টার ফলাফলের সর্বদা প্যারামিটারগুলির মধ্যে একটির মতো জীবনকাল থাকবে; নামযুক্ত লাইফটাইমগুলি কোন প্যারামিটারটি নির্দেশ করে।


4

যদি কোনও ফাংশন আর্গুমেন্ট হিসাবে দুটি রেফারেন্স গ্রহণ করে এবং কোনও রেফারেন্স দেয়, তবে ফাংশনটির প্রয়োগটি কখনও কখনও প্রথম রেফারেন্স এবং কখনও কখনও দ্বিতীয়টি ফিরে আসতে পারে। প্রদত্ত কলের জন্য কোন রেফারেন্স ফিরে আসবে তা অনুমান করা অসম্ভব। এই ক্ষেত্রে, প্রত্যাবর্তিত রেফারেন্সটির জন্য আজীবন অনুমান করা অসম্ভব, যেহেতু প্রতিটি যুক্তির রেফারেন্স পৃথক আজীবনের সাথে ভিন্ন ভিন্ন পরিবর্তনশীল বাঁধাইয়ের জন্য উল্লেখ করতে পারে। সুস্পষ্ট জীবনকাল এমন পরিস্থিতি এড়াতে বা স্পষ্ট করতে সহায়তা করে।

অনুরূপভাবে, যদি কোনও কাঠামো দুটি রেফারেন্স রাখে (দুটি সদস্য ক্ষেত্র হিসাবে) তবে কাঠামোর সদস্য ফাংশন কখনও কখনও প্রথম রেফারেন্স এবং কখনও কখনও দ্বিতীয়টি ফেরত দিতে পারে। আবার সুস্পষ্ট জীবনকাল এ জাতীয় অস্পষ্টতা রোধ করে।

কয়েকটি সাধারণ পরিস্থিতিতে, জীবনকালীন এলিস রয়েছে যেখানে সংকলকটি জীবনকাল নির্ধারণ করতে পারে।


1

আপনার উদাহরণটি কাজ না করার কারণটি কেবল কারণ মরচে কেবল স্থানীয় জীবনকাল এবং টাইপ অনুমান। আপনি যা পরামর্শ দিচ্ছেন তা বিশ্বব্যাপী অনুমানের দাবি। যখনই আপনার কাছে এমন একটি রেফারেন্স পাওয়া যায় যার আজীবন সম্মতি দেওয়া যায় না, তা অবশ্যই মন্তব্য করতে হবে।


1

মরচে আগন্তুক হিসাবে আমার বোধগম্যতা হ'ল সুস্পষ্ট জীবনকাল দুটি উদ্দেশ্য উপভোগ করে।

  1. কোনও কার্যক্রমে একটি স্পষ্ট আজীবন টীকা রাখলে সেই ফাংশনের অভ্যন্তরে যে ধরণের কোড উপস্থিত হতে পারে তা সীমাবদ্ধ করে। সুস্পষ্ট লাইফটাইম সংকলককে নিশ্চিত করতে অনুমতি দেয় যে আপনার প্রোগ্রামটি যা ইচ্ছা তা করছে।

  2. আপনি যদি (সংকলক) কোডের কোনও অংশটি বৈধ কিনা তা পরীক্ষা করতে (গুলি) চাইলে আপনাকে (সংকলক) পুনরাবৃত্তভাবে বলা প্রতিটি ফাংশনের ভিতরে তাকাতে হবে না। কোডটির এই অংশটি দ্বারা সরাসরি কল করা ফাংশনগুলির টীকাগুলিতে একবার নজর রাখা যথেষ্ট। এটি আপনার প্রোগ্রামটি আপনার (সংকলক) সম্পর্কে তর্ক করার পক্ষে অনেক সহজ করে তোলে এবং সংকলনের সময়কে পরিচালনাযোগ্য করে তোলে।

১ ম পয়েন্টে, পাইথনে লেখা নিম্নলিখিত প্রোগ্রামটি বিবেচনা করুন:

import pandas as pd
import numpy as np

def second_row(ar):
    return ar[0]

def work(second):
    df = pd.DataFrame(data=second)
    df.loc[0, 0] = 1

def main():
    # .. load data ..
    ar = np.array([[0, 0], [0, 0]])

    # .. do some work on second row ..
    second = second_row(ar)
    work(second)

    # .. much later ..
    print(repr(ar))

if __name__=="__main__":
    main()

যা মুদ্রণ করবে

array([[1, 0],
       [0, 0]])

এই ধরণের আচরণ আমাকে সর্বদা অবাক করে দেয়। যা ঘটছে তা হ'ল dfমেমরি ভাগ করে নেওয়া ar, সুতরাং যখন কিছু dfপরিবর্তনের বিষয়বস্তু হয় তখন workসেই পরিবর্তনটি সংক্রামিত arহয়। যাইহোক, কিছু ক্ষেত্রে এটি মেমরি দক্ষতার কারণে (কোনও অনুলিপি নয়) ঠিক আপনি যা চান তা হতে পারে। এই কোডটিতে আসল সমস্যাটি হ'ল ফাংশনটি second_rowদ্বিতীয়টির পরিবর্তে প্রথম সারিতে ফিরে আসবে; সৌভাগ্য যে ডিবাগিং।

পরিবর্তে মরচে লেখা একই ধরণের প্রোগ্রামটি বিবেচনা করুন:

#[derive(Debug)]
struct Array<'a, 'b>(&'a mut [i32], &'b mut [i32]);

impl<'a, 'b> Array<'a, 'b> {
    fn second_row(&mut self) -> &mut &'b mut [i32] {
        &mut self.0
    }
}

fn work(second: &mut [i32]) {
    second[0] = 1;
}

fn main() {
    // .. load data ..
    let ar1 = &mut [0, 0][..];
    let ar2 = &mut [0, 0][..];
    let mut ar = Array(ar1, ar2);

    // .. do some work on second row ..
    {
        let second = ar.second_row();
        work(second);
    }

    // .. much later ..
    println!("{:?}", ar);
}

এটি সংকলন, আপনি পাবেন

error[E0308]: mismatched types
 --> src/main.rs:6:13
  |
6 |             &mut self.0
  |             ^^^^^^^^^^^ lifetime mismatch
  |
  = note: expected type `&mut &'b mut [i32]`
             found type `&mut &'a mut [i32]`
note: the lifetime 'b as defined on the impl at 4:5...
 --> src/main.rs:4:5
  |
4 |     impl<'a, 'b> Array<'a, 'b> {
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^
note: ...does not necessarily outlive the lifetime 'a as defined on the impl at 4:5
 --> src/main.rs:4:5
  |
4 |     impl<'a, 'b> Array<'a, 'b> {
  |     ^^^^^^^^^^^^^^^^^^^^^^^^^^

প্রকৃতপক্ষে আপনি দুটি ত্রুটি পান, এর ভূমিকা 'a'bআন্তঃব্যবস্থাপনাগুলির মধ্যে একটিও রয়েছে । এর টীকাটি দেখে second_rowআমরা দেখতে পেলাম যে আউটপুটটি &mut &'b mut [i32]আজীবন 'b(দ্বিতীয় সারির জীবনকাল Array) সহ একটি রেফারেন্স হিসাবে বিবেচিত হবে । তবে, যেহেতু আমরা প্রথম সারিতে ফিরে আসছি (যার আজীবন রয়েছে 'a), সংকলক আজীবন মিলবে না বলে অভিযোগ করে। সঠিক জায়গায়। সঠিক সময়ে. ডিবাগিং একটি হাওয়া।


0

আমি উত্সর স্কোপে বৈধ থাকাকালীন কোনও প্রদত্ত রেফ সম্পর্কে চুক্তি প্রাপ্তির সুযোগে বৈধ ছিল বলে আমি আজীবন টীকাটি মনে করি। একই জীবনকালীন ধরণের আরও রেফারেন্স ঘোষণার ফলে স্কোপগুলি একত্রিত হয়, যার অর্থ সমস্ত উত্সের রেফিকে এই চুক্তিটি সন্তুষ্ট করতে হয়। এই জাতীয় টীকা সংকলককে চুক্তিটি পূরণের জন্য পরীক্ষা করার অনুমতি দেয়।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.