< 返回版块

henrylee2cn 发表于 2021-06-18 21:05

以下面一个 test 示例说明 tuple struct 的类型标识在不同的位置分别具有 type 与 fn pointer 双重身份:

#[test]
fn tuple_struct() {
    // Tuple 标识符具有双重身份,在类型位置时为 tuple struct,在函数位置时为函数指针 fn(i64, &'static str) -> Tuple {Tuple}
    struct Tuple(i64, &'static str);
    
    let t: Tuple =// 作为类型
        Tuple(0i64, "hi"); // 作为函数指针

    fn foo<T1, T2, U, F: FnOnce(T1, T2) -> U>(op: F) {}
    foo(Tuple); // 作为函数指针,且实现了 FnOnce(T1, T2) -> U,可以编译通过

    fn foo2<T1, T2, F: FnOnce(T1, T2)>(op: F) {}
    foo2(Tuple); // 作为函数指针,但未实现 FnOnce(T1, T2),无法编译通过,根据编译器提示可以验证 Tuple 的函数指针身份:
    
    // error[E0271]: type mismatch resolving `<fn(i64, &'static str) -> Tuple {Tuple} as FnOnce<(i64, &'static str)>>::Output == ()`
    //   --> src/lib.rs:92:5
    //    |
    //    |     fn foo2<T1, T2, F: FnOnce(T1, T2)>(op: F) {}
    //    |                        -------------- required by this bound in `foo2`
    //    |     foo2(Tuple); // 作为函数指针,但未实现 FnOnce(T1, T2),无法编译通过,根据编译器提示可以验证 Tuple 的函数指针身份                                  ...
    //    |     ^^^^ expected `()`, found struct `Tuple`
    
    foo(t); // t 只是 Tuple 类型的实例,不能像 Tuple 一样作为函数指针,无法编译通过:
    
    // error[E0277]: expected a `FnOnce<(_, _)>` closure, found `Tuple`
    //   --> src/lib.rs:89:9
    //    |
    //    |     fn foo<T1, T2, U, F: FnOnce(T1, T2) -> U>(op: F) {}
    //    |                          ------------------- required by this bound in `foo`
    // ...
    //    |     foo(t); // t 只是 Tuple 类型的实例,不能像 Tuple 一样作为函数指针,无法编译通过
    //    |         ^ expected an `FnOnce<(_, _)>` closure, found `Tuple`
    //    |
    //    = help: the trait `FnOnce<(_, _)>` is not implemented for `Tuple`
}

在 enum 中 tuple struct 枚举项同样具有 fn pointer 身份:

#[test]
fn enum_tuple_struct() {
    enum Enum {
        Tuple(i8, u8) // 具有第二身份 fn(i8, u8) -> tuple_struct::Enum {tuple_struct::Enum::Tuple}
    }
    let _: Enum = Enum::Tuple(0, 0); // 作为函数指针

    fn foo<T1, T2, U, F: FnOnce(T1, T2) -> U>(op: F) {}
    foo(Enum::Tuple); // 作为函数指针,且实现了 FnOnce(T1, T2) -> U,可以编译通过

    fn foo2<T1, T2, F: FnOnce(T1, T2)>(op: F) {}
    foo2(Enum::Tuple); // 作为函数指针,但未实现 FnOnce(T1, T2),无法编译通过,根据编译器提示可以验证 Enum::Tuple 的函数指针身份:

    // error[E0271]: type mismatch resolving `<fn(i8, u8) -> tuple_struct::Enum {tuple_struct::Enum::Tuple} as FnOnce<(i8, u8)>>::Output == ()`
    //   --> src/lib.rs:89:5
    //    |
    //    |     fn foo2<T1, T2, F: FnOnce(T1, T2)>(op: F) {}
    //    |                        -------------- required by this bound in `foo2`
    //    |     foo(Enum::Tuple);
    //    |     foo2(Enum::Tuple); // 作为函数指针,但未实现 FnOnce(T1, T2),无法编译通过,根据编译器提示可以验证 Enum::Tuple 的函数指针身份:                                   ...
    //    |     ^^^^ expected `()`, found enum `tuple_struct::Enum`
}

评论区

写评论
作者 henrylee2cn 2021-06-19 00:25

感谢楼上各位的指正与补充,我重新调整了一下文章内容

苦瓜小仔 2021-06-18 23:57

对的。根据你的代码,我又拓展了一下:

fn main() {
    fn fn_<U, F: Fn() -> U>(f: F) -> U { f() }
    fn fn_once<U, F: FnOnce() -> U>(_: F) {}
    fn fn_mut<U, F: FnMut() -> U>(_: F) {}

    struct Tuple();
    let _a = fn_(Tuple); // _a: Tuple
    fn_once(Tuple);
    fn_mut(Tuple);

    enum A {
        Tuple(),
    }
    let _a = fn_(A::Tuple); // _a: A
    fn_once(A::Tuple);
    fn_mut(A::Tuple);
}

我记得在 Rust Book 里面有个例子的代码就用到元组结构体的构造函数性质。

是一个很小的知识点。

--
👇
chirsz-ever: tuple struct 的类型名能当函数用,第二个例子改成:

#[test]
fn tuple_struct_2() {
    struct Tuple2();
    fn foo2<U, F: FnOnce() -> U>(op: F) {}
    foo2(Tuple2)
}

就能通过编译了。

viruscamp 2021-06-18 22:08
#[test]
fn tuple_struct_1() {
    struct Tuple(i64, &'static str);
    fn foo<T1, T2, U, F: FnOnce(T1, T2) -> U>(op: F) {}
    foo(Tuple);
    let t = Tuple(0i64, "hi"); // 这个看起像函数调用吧?当你想把 Tuple 作为函数使用时,就创建一个函数,相当于C++的构造函数。
    foo(t); // 如果这个能过的话,才叫 struct Tuple 实现了 FnOnce
}
chirsz-ever 2021-06-18 21:52

tuple struct 的类型名能当函数用,第二个例子改成:

#[test]
fn tuple_struct_2() {
    struct Tuple2();
    fn foo2<U, F: FnOnce() -> U>(op: F) {}
    foo2(Tuple2)
}

就能通过编译了。

1 共 4 条评论, 1 页