码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • [Rust笔记] 代码写明 Rust 中的泛型型变


    代码写明 Rust 中的泛型型变

    Variance译作型变或可变性或变体.
    表示"泛型类型的子类型关系"如何从"泛型参数的子类型关系"中推导.

    定义

    无法理解时, 不要深究, 看完后再读一遍此定义即可.

    • 假设1 C是一个泛型类或接口, T是类型参数.

    • 假设2 类型Dog是Animal的子类型.

    • 定义1 Covariance/Covariant译作协变:
      如果C是C的子类型, 那么C对T协变.

    • 定义2 Contravariance/Contravariant译作逆变:
      如果C是C的子类型, 那么C对T逆变.

    • 定义3 Invariance/Invariant译作不变, 或译作抗变:
      C对T既不是协变也不是逆变, 那么C对T不变.

    子类型的证明

    1. let mut t1: T1 = make_t1();
    2. let t2: T2 = make_t2();
    3. // 在排除 type coerced 类型强制转换后, 以下证明有效
    4. // 1. 赋值证明
    5. let _t1: T1 = t2; // 证明`T2` 是 `T1` 的子类型
    6. t1 = t2; // 可赋值, 证明`T2` 是 `T1` 的子类型
    7. // 2. 函数调用证明
    8. fn use_t1(v: T1) {}
    9. use_t1(t2); // 实参类型 `T2` 可以替代形参类型 `T1`, 证明 `T2` 是 `T1` 的子类型

    函数调用证明的适用范围更广:

    • 许多类型写不出, 比如闭包

    • 自动推导的生存期写不出

    • 许多泛型参数是调用处才能确定类型

    必须排除 type coerced 类型强制转换:

    1. let string1: String = String::from("abc");
    2. let str1: &str = &string1; // 不能证明 `&String` 是 `&str` 的子类型

    生存期子类型

    Rust 没有实际类型 struct, enum 和 union 的继承,
    子类型关系只体现在生存期上, 可以通过赋值来证明.

    子类型的值可以转型为父类型:

    1. fn lifetime_subtype<'long: 'short, 'short, T: Copy>(a: &'short T, b: &'long T) {
    2. let _long_to_short: &'short T = b; // 成功 子类型的值可以转型为父类型
    3. }

    泛型参数'long: 'short定义'long是'short的子类型,
    意味着'long是一个较长的生存期, 它能完全覆盖'short这个较短的生存期,
    那么任何一个需要&'short i32的地方(转型,赋值,参数)&'long i32都可以满足的,
    所以&'long i32是&'short i32的子类型.

    父类型的值不可以转型为子类型:

    1. fn lifetime_subtype<'long: 'short, 'short, T: Copy>(a: &'short T, b: &'long T) {
    2. let _short_to_long: &'long T = a; // 失败 父类型的值不可以转型为子类型
    3. }

    较复杂的代码:

    1. fn lifetime_subtype<'long: 'short, 'short, T: Copy>(a: &'short mut T, b: &'long T) {
    2. *a = *b;
    3. }
    4. static I_STATIC: i32 = 1; // 其生存期为 'static
    5. fn main() {
    6. let mut i_1 = 2; // 假设其自动推导生存期为 '1
    7. {
    8. let mut i_2 = 3; // 假设其自动推导生存期为 '2
    9. dbg!(I_STATIC, i_1, i_2);
    10. //lifetime_subtype(&mut i_1, &i_2); // 无法编译
    11. lifetime_subtype(&mut i_2, &i_1); // 子类型关系为 `'1: '2` 满足函数泛型条件 `'long: 'short`
    12. dbg!(i_2);
    13. }
    14. lifetime_subtype(&mut i_1, &I_STATIC); // 子类型关系为 `'static: '1`
    15. dbg!(I_STATIC, i_1);
    16. }

    以上代码说明:

    1. 许多类型和生存期参数是 rustc 自动推导的, 我们无法明确的写出

    2. 自动推导出的生存期符合子类型关系

    3. 静态生存期&'static T是任意生存期&'x T的子类型

    Rust 协变 逆变 不变

    Vec对T协变

    编译成功 证明 Vec<&'long i32'> 是 Vec<&'short i32> 的子类型

    1. fn lifetime_covariant<'long: 'short, 'short>(a: &'short i32, b: &'long i32) {
    2. //! `'long` 是 `'short` 的子类型, `&'long i32` 是 `&'short i32` 的子类型
    3. let mut vec_long: Vec<&'long i32> = vec![b];
    4. let mut vec_short: Vec<&'short i32> = vec![a];
    5. vec_short = vec_long; // 成功
    6. }

    编译失败 证明 Vec<&'short i32'> 不是 Vec<&'long i32> 的子类型

    1. fn lifetime_covariant<'long: 'short, 'short>(a: &'short i32, b: &'long i32) {
    2. let mut vec_long: Vec<&'long i32> = vec![b];
    3. let mut vec_short: Vec<&'short i32> = vec![a];
    4. vec_long = vec_short; // 失败
    5. }

    Cell对T不变

    编译失败 证明 Cell<&'short i32'> 不是 Cell<&'long i32> 的子类型

    1. use std::cell::Cell;
    2. fn lifetime_invariant<'long: 'short, 'short>(a: &'short i32, b: &'long i32) {
    3. let mut cell_long: Cell<&'long i32> = Cell::new(b);
    4. let mut cell_short: Cell<&'short i32> = Cell::new(a);
    5. cell_short = cell_long; // 失败
    6. }

    编译失败 证明 Cell<&'long i32'> 不是 Cell<&'short i32> 的子类型

    1. use std::cell::Cell;
    2. fn lifetime_invariant<'long: 'short, 'short>(a: &'short i32, b: &'long i32) {
    3. let mut cell_long: Cell<&'long i32> = Cell::new(b);
    4. let mut cell_short: Cell<&'short i32> = Cell::new(a);
    5. cell_long = cell_short; // 失败
    6. }

    Fn -> R对A逆变

    编译成功 证明 Fn(&'a str) -> bool 是 Fn(&'static str) -> bool 的子类型

    1. fn lifetime_fn_contravariant<'outer>(str_outer: &'outer str) {
    2. let str_static: &'static str = "static";
    3. fn compare_with_static(instr: &'static str) -> bool {
    4. instr == "abc"
    5. } // 类型 `Fn(&'static str) -> bool`
    6. fn make_compare_closure<'x>(a: &'x str) -> impl Fn(&'x str) -> bool {
    7. return move |instr: &'x str| { instr == a }
    8. } // 返回值类型 `Fn(&'x str) -> bool`
    9. struct S<'z>(&'z str);
    10. impl<'z> S<'z> {
    11. fn do_compare'z str) -> bool>(&self, f: F) -> bool {
    12. f(self.0)
    13. }
    14. }
    15. let s_static: S<'static> = S("xyz"); // `s_static.do_compare` 参数类型为 `Fn(&'static str) -> bool`
    16. s_static.do_compare(compare_with_static); // 类型相符, 当然可以用 `Fn(&'static str) -> bool` 做参数
    17. s_static.do_compare(make_compare_closure(str_static)); // 类型相符
    18. s_static.do_compare(make_compare_closure(str_outer)); // 逆变, 实参类型为 `Fn(&'outer str) -> bool`
    19. let s_outer: S<'outer> = S(str_outer); // `s_outer.do_compare` 参数类型为 `Fn(&'outer str) -> bool`
    20. //s_outer.do_compare(compare_with_static); // 协变失败
    21. //s_outer.do_compare(make_compare_closure(str_static)); // 协变失败
    22. s_outer.do_compare(make_compare_closure(str_outer)); // 类型相符
    23. {
    24. let string_inner = String::from("inner"); // 命名其生存期为 'inner
    25. let str_inner: &str = string_inner.as_str();
    26. s_static.do_compare(make_compare_closure(str_inner)); // 逆变, 实参`Fn(&'inner str) -> bool` 替代形参 `Fn(&'static str) -> bool`
    27. s_outer.do_compare(make_compare_closure(str_inner)); // 逆变, 实参`Fn(&'inner str) -> bool` 替代形参 `Fn(&'outer str) -> bool`
    28. }
    29. // 强制拉长生存期
    30. s_outer;
    31. s_static;
    32. str_outer;
    33. }

    Fn -> R对R协变

    编译成功 证明 Fn() -> &'static str 是 Fn() -> &'a str 的子类型

    1. fn lifetime_fn_covariant<'outer>(str_outer: &'outer str) {
    2. let str_static: &'static str = "static";
    3. fn return_static() -> &'static str {
    4. "abc"
    5. } // 类型 `Fn() -> &'static str`
    6. fn make_return_closure<'x>(a: &'x str) -> impl Fn() -> &'x str {
    7. return move || { a }
    8. } // 返回值类型 `Fn() -> &'x str`
    9. struct S<'z>(&'z str);
    10. impl<'z> S<'z> {
    11. fn set_with &'z str>(&mut self, f: F) -> () {
    12. self.0 = f();
    13. }
    14. }
    15. let mut s_static: S<'static> = S("xyz"); // `s_static.set_with` 参数类型为 `Fn() -> &'static str`
    16. s_static.set_with(return_static); // 类型相符, 当然可以用 `Fn() -> &'static str` 做参数
    17. s_static.set_with(make_return_closure(str_static)); // 类型相符
    18. //s_static.set_with(make_return_closure(str_outer)); // 逆变失败
    19. let mut s_outer: S<'outer> = S(str_outer); // `s_outer.set_with` 参数类型为 `Fn() -> &'outer str`
    20. //s_outer.set_with(return_static); // 理论可以协变, 实际会导致 `s_outer` 类型推断成 `S<'static>`, 然后编译失败, 无法达到目的
    21. s_outer.set_with(make_return_closure(str_static)); // 协变, 实参`Fn() -> &'static str` 替代形参 `Fn() -> &'outer str`
    22. s_outer.set_with(make_return_closure(str_outer)); // 类型相符
    23. {
    24. let string_inner = String::from("inner"); // 命名其生存期为 'inner
    25. let str_inner: &str = string_inner.as_str();
    26. let mut s_inner: S = S(str_inner); // `s_inner.set_with` 参数类型为 `Fn() -> &'inner str`
    27. //s_inner.set_with(return_static); // 理论可以协变, 实际会导致 `s_inner` 类型推断成 `S<'static>`, 然后编译失败, 无法达到目的
    28. s_inner.set_with(make_return_closure(str_static)); // 协变, 实参`Fn() -> &'static str` 替代形参 `Fn() -> &'inner str`
    29. s_inner.set_with(make_return_closure(str_outer)); // 协变, 实参`Fn() -> &'outer str` 替代形参 `Fn() -> &'inner str`
    30. s_inner.set_with(make_return_closure(str_inner)); // 类型相符
    31. }
    32. // 强制拉长生存期
    33. s_outer;
    34. s_static;
    35. str_outer;
    36. }

    Rust 泛型类型型变的推导

    Rust 泛型类型型变不是由语法定义,而是固定的几个基础类型的可变性表, 然后组合类型 struct, enum 和 union 根据其包含域类型的可变性确定, 域类型有多种可变性时, 组合类型为不变.

    TypeVariance in 'aVariance in T
    &'a Tcovariantcovariant
    &'a mut Tcovariantinvariant
    *const T
    covariant
    *mut T
    invariant
    [T] and [T; n]
    covariant
    fn() -> T
    covariant
    fn(T) -> ()
    contravariant
    std::cell::UnsafeCell
    invariant
    std::marker::PhantomData
    covariant
    dyn Trait + 'acovariantinvariant

    型变推导实例

    1. Cell 包含 std::cell::UnsafeCell 其对T不变.

    2. Vec 包含 alloc::raw_vec::RawVec 包含 core::ptr::Unique 包含 std::marker::PhantomData 其对T协变.

    3. 推导以下代码中泛型类型的型变

    1. use core::ptr::NonNull;
    2. struct Node(T);
    3. type Link1 = Option>>; // `NonNull` 就是 `*const T`, 对 `Node` 协变, 最终对 `T` 协变
    4. type Link2 = *mut Node; // 对 `Node` 不变, 最终对 `T` 不变

    ---

    重新研究后发现,无法证明 trait 的子类型关系存在逆变, 那么就不要用协变逆变解释.

    1. trait Animal {}
    2. trait Cat : Animal {}
    3. fn use_fn(f: F) {
    4. }
    5. fn use_fn_2(f: F) {
    6. use_fn(f); // 错误 那么就不是逆变关系
    7. }

  • 相关阅读:
    MySQL8.0 索引优化-invisible index
    【论文精读】TextDiffuser-2:释放语言模型用于文本渲染的力量
    国际经济合作知识点归纳
    游戏陪玩系统app
    HackTheBox Ambassador 枚举获得用户shell,git consul API提权
    将swagger注解导入apifox的IDEA配置
    代码随想录二刷 Day 29
    电商小程序10分类管理
    一键部署区块链背后的秘密(下)
    redis的原理和源码-服务端的介绍和源码解析(命令请求的执行过程、服务器初始化过程)
  • 原文地址:https://blog.csdn.net/u012067469/article/details/128030213
  • 最新文章
  • 攻防演习之三天拿下官网站群
    数据安全治理学习——前期安全规划和安全管理体系建设
    企业安全 | 企业内一次钓鱼演练准备过程
    内网渗透测试 | Kerberos协议及其部分攻击手法
    0day的产生 | 不懂代码的"代码审计"
    安装scrcpy-client模块av模块异常,环境问题解决方案
    leetcode hot100【LeetCode 279. 完全平方数】java实现
    OpenWrt下安装Mosquitto
    AnatoMask论文汇总
    【AI日记】24.11.01 LangChain、openai api和github copilot
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1
正则表达式工具 cron表达式工具 密码生成工具

京公网安备 11010502049817号