2017-05-25 44 views
1

在許多語言中,一個常見的構造函數用法是初始化使用的語法類似這樣的僞對象的值:如何初始化結構與一系列的參數

constructor Foo(args...) { 
    for arg { 
     object.arg = arg 
    } 
} 

鏽起初似乎也不例外。爲struct許多impl包括一個名爲new拉上一系列有序的參數到結構領域的構造函數:

struct Circle { 
    x: i32, 
    y: i32, 
    radius: i32, 
} 

impl Circle { 
    fn new(x: i32, y: i32, radius: i32) -> Circle { 
     Circle { x: x, y: y, radius: radius } 
    } 
} 

與宏可能看起來像zip!(Circle, 52, 32, 5)這樣做。它會將值依次壓縮到Circle的字段中。 zip!(Circle, 52, 32)zip!(Circle, 52, 32, 5, 100)都會出現問題,但是像這樣的宏將是一種非常靈活的方法,可以將值推送到任何結構的新實例,而不需要太多的樣板。

有沒有一種習慣的方法來簡化這個樣板?如何將一系列有序參數映射到結構的每個字段上而不明確寫入樣板代碼來這樣做?

回答

3

這是不可能與一個非常簡單的原因宏:宏無法想象中的字段名憑空。

最簡單的解決辦法,如果你是舒適暴露你的類型的細節,正在田野市民:

struct Circle { 
    pub x: i32, 
    pub y: i32, 
    pub radius: i32, 
} 

fn main() { 
    let circle = Circle { x: 3, y: 4, radius: 5 }; 
} 

也就是說,沒有需要有一個構造函數,它工作完全正常沒有一個。畢竟,如果構造函數除了傳遞值之外沒有別的事情,那構造函數本身就是毫無意義的,不是嗎?

如果您希望提供更短的初始化語法,比如,你可以:

use std::convert::From; 

impl From<(i32, i32, i32)> for Circle { 
    fn from(t: (i32, i32, i32)) -> Circle { 
     Circle { x: t.0, y: t.1, radius: t.2 } 
    } 
} 

fn main() { 
    let circle: Circle = (3, 4, 5).into(); 
} 

也一般,類型推斷應該不必拼出: Circle緩解你。

但我會注意到,這更容易出錯,因爲交換兩個參數而不注意會容易得多。你可能想要堅持顯式名稱,或者引入顯式類型。

+0

謝謝你,我不知道'From'特性,它揭示了我希望使用的那種界面。我發現的另一個選項,如果命名字段不重要,則使用元組結構:'Circle(i32,i32,i32)'。一個適應性較差的解決方案是從配置文件反序列化,雖然這有利於默認情況下使多個不同的實例可用,並且易於包含更多。 – Aaron3468

3

我不確定你可以(或者應該)依賴宏中結構的字段順序。

但也許類似於你想要的,因爲它節省了構造函數樣板,是derive_builder crate

您可以使用它像這樣:

#[macro_use] 
extern crate derive_builder; 

#[derive(Builder, Debug)] 
struct Circle { 
    x: i32, 
    y: i32, 
    radius: i32, 
} 

fn do_stuff() -> Result<(), String> { 
    let c = CircleBuilder::default() 
     .x(2) 
     .y(4) 
     .radius(123) 
     .build()?; 

    println!("  x = {}", c.x); 
    println!("  y = {}", c.y); 
    println!("radius = {}", c.radius); 

    Ok(()) 
} 

注意Result在調用功能和來電來build()?

確保有這個在您的Cargo.toml

[dependencies] 
derive_builder = "0.4.7"