2016-09-01 4 views
7

मेरे पास एक संरचना Foo है जो बाह्य धारावाहिक प्रारूप का प्रतिनिधित्व करती है। Foo में दर्जनों फ़ील्ड हैं, और हर समय अधिक जोड़े जाते हैं। खुशी से, सभी नए क्षेत्रों को समझदार डिफ़ॉल्ट मान होने की गारंटी है।जंग के ढांचे के सार्वजनिक क्षेत्रों को अद्यतन करना जिनमें निजी फ़ील्ड हैं

जंग एक struct बनाने मूलभूत मूल्यों का उपयोग कर और फिर कुछ ही चुने गए मानों अद्यतन करने के लिए एक अच्छा वाक्य रचना है:

Foo { 
    bar: true, 
    ..Default::default() 
} 
इसी

, हम "के इस struct उसे भावी संस्करण में अधिक क्षेत्रों में हो सकता है विचार का प्रतिनिधित्व कर सकते "PhantomData प्रकार के निजी क्षेत्र का उपयोग करना।

लेकिन अगर हम इन दोनों मुहावरे गठबंधन है, हम कोई त्रुटि मिलती है:

use std::default::Default; 

mod F { 
    use std::default::Default; 
    use std::marker::PhantomData; 

    pub struct Foo { 
     pub bar: bool, 
     phantom: PhantomData<()>, 
    } 

    impl Default for Foo { 
     fn default() -> Foo { 
      Foo { 
       bar: false, 
       phantom: PhantomData, 
      } 
     } 
    } 
} 

fn main() { 
    F::Foo { 
     bar: true, 
     ..Default::default() 
    }; 
} 

यह हमें त्रुटि देता है:

error: field `phantom` of struct `F::Foo` is private [--explain E0451] 
    --> <anon>:23:5 
    |> 
23 |>  F::Foo { 
    |> ^

तार्किक रूप से, मैं, का तर्क था कि यह काम करना चाहिए, क्योंकि हम केवल सार्वजनिक क्षेत्रों को अद्यतन कर रहे हैं, और यह उपयोगी मुहावरे होगा। विकल्प कुछ का समर्थन करना है:

Foo::new() 
    .set_bar(true) 

... जो दर्जनों क्षेत्रों के साथ थकाऊ हो जाएगा।

मैं इस समस्या के आसपास कैसे काम कर सकता हूं?

+2

'प्रेत' को '__phantom 'नाम दें, इसे सार्वजनिक बनाएं और' # [डॉक्टर (छुपाएं)] '।लाइव उदाहरण: ['std :: io :: ErrorKind :: __ nonexhaustive'] (https://doc.rust-lang.org/src/std/up/src/libstd/io/error.rs.html#162- 168) – mcarton

+2

@mcarton ध्यान दें कि 'std' थोड़ा सा विशेष है कि यह' __Nonexhaustive' संस्करण को अस्थिर होने की घोषणा कर सकता है, ताकि कॉलर सचमुच इसका उपयोग नहीं कर सकें यदि वे स्थिर जंग पर हैं। लाइब्रेरी लेखकों ने एक ही चाल को नियोजित किया (जो कुछ मैं निश्चित रूप से करता हूं) को सम्मेलन पर भरोसा करना चाहिए। (जो मुझे नहीं लगता कि अभ्यास में एक वास्तविक समस्या है, मैं सिर्फ एक पेडेंट हूं।) – BurntSushi5

+0

@emk मैं ** ** ** 'फैंटॉमडाटा' के बारे में सीख रहा था। इस उपयोगी उदाहरण के लिए धन्यवाद! – ljedrz

उत्तर

4

__phantom को phantom का नाम बदलें, यह सार्वजनिक और #[doc(hidden)] बनाते हैं। std::io::ErrorKind::__Nonexhaustive:

use std::default::Default; 

mod foo { 
    use std::default::Default; 
    use std::marker::PhantomData; 

    pub struct Foo { 
     pub bar: bool, 

     // We make this public but hide it from the docs, making 
     // it private by convention. If you use this, your 
     // program may break even when semver otherwise says it 
     // shouldn't. 
     #[doc(hidden)] 
     pub _phantom: PhantomData<()>, 
    } 

    impl Default for Foo { 
     fn default() -> Foo { 
      Foo { 
       bar: false, 
       _phantom: PhantomData, 
      } 
     } 
    } 
} 

fn main() { 
    foo::Foo { 
     bar: true, 
     ..Default::default() 
    }; 
} 

यह एक नहीं तो असामान्य पैटर्न, लाइव उदाहरण है।

निश्चित रूप से, उपयोगकर्ताओं को __named फ़ील्ड का उपयोग करने का विकल्प चुनने पर कोई चेतावनी या कुछ भी नहीं होगा, लेकिन __ इरादा स्पष्ट रूप से स्पष्ट करता है। यदि एक चेतावनी की आवश्यकता है, #[deprecated] का उपयोग किया जा सकता है।

+0

धन्यवाद! मैंने आपका जवाब स्वीकार कर लिया क्योंकि यह सीधे सवाल का जवाब देता है, लेकिन @ क्रिस एमर्सन का उत्तर उच्च स्तरीय एपीआई के लिए एक अच्छा विकल्प प्रदान करता है। – emk

6

डिफ़ॉल्ट फ़ील्ड वाक्यविन्यास काम नहीं करता है क्योंकि आप अभी भी एक नया उदाहरण बना रहे हैं (भले ही आप किसी अन्य ऑब्जेक्ट से फ़ील्ड मानों को लेने का प्रयास कर रहे हों)।

Foo::new() 
    .set_bar(true) 
    .set_foo(17) 
    .set_splat("Boing") 

काफी अधिक कठिन है की तुलना में:

Foo { 
    bar: true, 
    foo: 17, 
    splat: "Boing", 
    ..Foo::default() 
} 

वैकल्पिक रूप से, आप में सार्वजनिक क्षेत्रों को अलग कर सकता है

The alternative is to support something like:

Foo::new() 
    .set_bar(true) 

...which will get tedious with dozens of fields.

मुझे यकीन है कि यहां तक ​​कि कई क्षेत्रों के साथ, यह नहीं कर रहा हूँ उनका अपना प्रकार:

pub struct FooPub { 
    pub bar: bool, 
    // other pub fields 
} 

pub struct Foo { 
    pub bar: bool, 
    // other pub fields 
    // alternatively, group them: pub public: FooPub, 

    foo: u64, 
} 

impl Foo { 
    pub fn new(init: FooPub) { 
     Foo { 
      bar: init.bar, 
      // other pub fields 
      // alternative: public: init 

      // private fields 
      foo: 17u64, 
     } 
    } 
} 

फिर आप के रूप में इसे कहते हैं:

Foo::new(FooPub{ bar: true }) 

या एक fn FooPub::default() जोड़ने आप में से कुछ फ़ील्ड डिफ़ॉल्ट जाने के लिए:

Foo::new(FooPub{ bar: true, ..FooPub::default()}) 
+0

धन्यवाद! अपेक्षाकृत कुछ क्षेत्रों के साथ उच्च स्तरीय एपीआई के लिए ये सभी प्यारे समाधान हैं। लेकिन मेरे मामले में, मैं 'सेर्डे' के साथ deserialized एक तृतीय पक्ष फ़ाइल प्रारूप का प्रतिनिधित्व करने वाली बहुत कम-स्तरीय संरचना के साथ काम कर रहा हूं, और मुझे ऐसा कुछ चाहिए जो दर्जनों दुर्लभ रूप से उपयोग किए जाने वाले फ़ील्ड के साथ काम करता है, और जिसके लिए मुझे आवश्यकता नहीं है दो में संरचना विभाजित करने के लिए। लेकिन आपका जवाब सरल समस्याओं वाले लोगों की मदद करेगा। धन्यवाद! – emk

संबंधित मुद्दे