默认通用参数

时间:2016-05-18 22:11:54

标签: rust traits

我有一个可以使用构建器模式构造的结构,因为有一些Option字段。

如果我使用构建器函数指定这些可选字段,则不必指定通用参数。

但如果我不调用这些函数,我需要指定通用参数。

以下是一个例子:

use Structs::*;

struct Struct<T, F: Fn(T)> {
    func: Option<F>,
    value: T,
}

enum Structs<T, F: Fn(T)> {
    Struct1(T),
    Struct2(T, F),
}

impl<T, F: Fn(T)> Struct<T, F> {
    fn new(value: T) -> Struct<T, F> {
        Struct {
            func: None,
            value: value,
        }
    }

    fn build(self) -> Structs<T, F> {
        if let Some(func) = self.func {
            Struct2(self.value, func)
        }
        else {
            Struct1(self.value)
        }
    }

    fn func(mut self, func: F) -> Struct<T, F> {
        self.func = Some(func);
        self
    }
}

fn main() {
    let _strct = Struct::new(42)
        .func(|n| { println!("{}", n); })
        .build();

    //let _strct = Struct::new(42).build(); // Does not compile.
    let _strct = Struct::<_, &Fn(_)>::new(42).build();
}

我想在未设置可选字段时省略类型注释,如下所示:

let _strct = Struct::new(42).build();

应指定F类型取决于T

我尝试指定默认类型参数:

impl<T, F: Fn(T) = Box<Fn(T)>> Struct<T, F> {

但它没有解决问题。

那么我怎样才能避免在Struct::new()调用中指定类型参数?

如果无法避免这种情况,是否有任何构建器模式的替代方法可以省略类型注释?

2 个答案:

答案 0 :(得分:5)

通过在构建过程中更改构建器的类型,可以解决此问题。由于Struct::func获取构建器的所有权并返回新构建器,因此我们可以自由更改结果类型。

首先,我们需要为F指定初始类型。我们可以选择Fn(T)的任何现有实现,但我们可以做得更好。我建议我们使用empty/void/uninhabited/bottom type,以便明确当F为该类型时,OptionNone(您可以&#39;}构造一个Some(x),因为对于空类型没有有效的x。这种方法的一个缺点是,对于类型(闭包除外)实现FnFnMutFnOnce是不稳定的,需要每晚编译器。

#![feature(fn_traits)]
#![feature(unboxed_closures)]

enum Void {}

impl<T> FnOnce<T> for Void {
    type Output = ();

    extern "rust-call" fn call_once(self, _args: T) {
        match self {}
    }
}

impl<T> FnMut<T> for Void {
    extern "rust-call" fn call_mut(&mut self, _args: T) {
        match *self {}
    }
}

impl<T> Fn<T> for Void {
    extern "rust-call" fn call(&self, _args: T) {
        match *self {}
    }
}

接下来,让我们将Struct::new移到另一个impl区块:

impl<T> Struct<T, Void> {
    fn new(value: T) -> Struct<T, Void> {
        Struct {
            func: None,
            value: value,
        }
    }
}

implF 通用newStruct只生成F = Void func。这样可以避免在永远不会调用func的情况下出现歧义。

最后,我们需要让impl<T, F0: Fn(T)> Struct<T, F0> { fn func<F1: Fn(T)>(self, func: F1) -> Struct<T, F1> { Struct { func: Some(func), value: self.value, } } } 更改构建器的类型:

impl

此方法需要保留在F Struct<T, F> func func类型参数上的self块中,以便可以在Struct上的构建器上使用已被调用。但是,Struct<T, F0>本身也必须是通用的,以便它可以接收任何类型的函数(而不是与构建器类型匹配的函数)。然后,我们不必改变Struct<T, F1>,而是构建一个新的 vc.twitterLogin = { session, error in if let session = session { let credential = FIRTwitterAuthProvider.credentialWithToken(session.authToken, secret: session.authTokenSecret) FIRAuth.auth()?.signInWithCredential(credential) { [unowned self] user, error in if let error = error { print(error.userInfo["NSUnderlyingError"]) } if user != nil { self.navCtrl.popViewControllerAnimated(true) self.delegate?.didAuthenticate(self) } } } } ,因为我们不能将reqrep ^([^:\ ]+\ +)/(/.+)$ \1\2 if { path_beg // } 强制转换为dependencies { compile 'com.google.android.gms:play-services:9.0.0' }

答案 1 :(得分:2)

关注Francis Gagné's clever solution,这里有一个类似的想法可以用于稳定的Rust:

struct Struct<T, F: Fn(T)> {
    func: Option<F>,
    value: T,
}

enum Structs<T, F: Fn(T)> {
    Struct1(T),
    Struct2(T, F),
}

impl<T> Struct<T, fn(T)> {
    fn new(value: T) -> Struct<T, fn(T)> {
        Struct {
            func: None,
            value: value,
        }
    }
}

impl<T, F: Fn(T)> Struct<T, F> {
    fn func<F2: Fn(T)>(self, func: F2) -> Struct<T, F2> {
        Struct {
            func: Some(func),
            value: self.value,
        }
    }

    fn build(self) -> Structs<T, F> {
        use Structs::*;

        if let Some(func) = self.func {
            Struct2(self.value, func)
        } else {
            Struct1(self.value)
        }
    }
}

fn main() {
    let _strct = Struct::new(42)
        .func(|n| {
            println!("{}", n);
        })
        .build();

    let _strct = Struct::new(42).build();
}

我们只是说我们返回一个为函数指针参数化的结构,而不是一个清晰的Void类型。您也可以指定参考特征对象:

impl<T> Struct<T, &'static Fn(T)> {
    fn new(value: T) -> Struct<T, &'static Fn(T)> {

从评论中回答我自己的问题:

  

如果没有为TF指定具体类型,那么Rust编译器应分配多少空间来存储Struct

64位计算机上fn()的大小为8个字节,导致整个结构总共16个字节:

std::mem::size_of::<fn()>();
std::mem::size_of_val(&strct);

但是,当你给它一个具体的回调时,结构只需要8个字节!这是因为类型将为回调单态化,它不需要状态,基本上可以内联。

FrancisGagné的解决方案在每种情况下只需要8个字节,因为Void类型的大小为零!