惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

The GitHub Blog
The GitHub Blog
IT之家
IT之家
B
Blog RSS Feed
罗磊的独立博客
GbyAI
GbyAI
博客园 - Franky
Y
Y Combinator Blog
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
Google DeepMind News
Google DeepMind News
博客园 - 聂微东
N
Netflix TechBlog - Medium
博客园 - 三生石上(FineUI控件)
人人都是产品经理
人人都是产品经理
U
Unit 42
博客园 - 叶小钗
Jina AI
Jina AI
MyScale Blog
MyScale Blog
雷峰网
雷峰网
B
Blog
Hugging Face - Blog
Hugging Face - Blog
Blog — PlanetScale
Blog — PlanetScale
Recent Announcements
Recent Announcements
腾讯CDC
酷 壳 – CoolShell
酷 壳 – CoolShell

Whexy Blog

关于身体 替尔泊肽 We lost the AIxCC. So, what now? Arm VMM with Apple's Hypervisor Framework Driving WaveShare E‐Paper Display with a Raspberry Pi Pico in MicroPython Use cgroup v2 inside docker containers Annual Hit Piece: Fuzzing Top Conference Paper Debunking Report Solving SSH Key Login Issues on Synology NAS Can SSD Cache Improve Synology NAS Write Speeds? Virtualization is all you need Running Windows Games on Mac Without Virtual Machines Tears of the Kingdom: End of an Era Anonymous CDN Traffic Relay Self-host Relay Service with CDN Home Networking Solution Building Your Own Blog System Connecting Smart Devices to SUSTech Campus Network Function Color Theory Stop Forkin' Around: Faster Creating of Large Processes on Linux PMU Interrupts: How to handle them Asynchronous Mutex Using QEMU to run Linux images on M1 Macbook Alligator In Vest - My first research work Experience Using Several Plugins in Complex LaTeX Projects Understanding Rust Generic Traits SUSTeam: Ultimate Gaming Platform Inline Assembly Language in C React Learning Notes Building a School Bus Schedule App for Apple Watch 12307 Train Ticket Purchase Platform
Variance in Rust
Whexy · 2021-02-21 · via Whexy Blog

Whexy /

February 21, 2021

I’m watching Jon Gjengset’s live coding stream. And the topic is “Subtyping and Variance”. This is my note.

Jon Gjengset’s live coding stream

The Rust compiler will automatically shrink the lifetime of the parameter to the shortest one. For example,

fn main() {
	let s = String::new();
	let x = "static str"; // `x` is `&'static str`
	let mut y = &*s; // `y` is `&'s str`
	y = x;
	// Still compilable!
  // Rust automatically shrink the lifetime static to s.
}

That makes sense, because you can always trust a value from who lives longer, without concerning about the value somehow goes invalid. What’s behind the scene is that Rust have a system of subtyping and variance.

Just think of an example in Java, class Cat is a subtype of the class Animal. In brief, we say T is a subtype of U (notation T <: U) when T is at least as useful as U. T can do anything that U can do, but T may have the ability of other things. In Rust, the lifetime ’static is a subtype of every lifetime. Rust compiler then uses different variance rules to check whether the program should be compiled or not.

You may understand variance in many other programming languages. And there are three types of variance in programming, called covariance, contra-variance, and invariance.

Covariance

Covariance is the most common case. Most things in Rust is covariance. For example,

/// define a function which takes an lifetime sticker `a`
fn foo(_: &'a str) {}

// and you can call the function with
foo(&'a str);
// or
foo(&'static str);

In the example, we can give the function with parameter whose lifetime is no matter a or static. That is because static is a subtype of a. The static str lives longer than the required ’a, so there should be no concern that the borrowed variable would be unexpectedly dropped.

Contra-variance

Let’s consider the high-rank function example below.

/// define a function which takes a function,
/// which takes a lifetime sticker `a`.
fn foo(bar: Fn(&'a str) -> ()) {
	bar(str);
}

let x : Fn(&'a str) -> ();
foo(x); // that makes sense.

let y : Fn(&'static str) -> ();
foo(y); // should that make sense ???

Should foo(y) be compilable? Definitely not! Let’s say if foo(y) compiles, then we are actually doing things in the high-rank function like:

let baz = &*String::new();
// lifetime of baz is shorter than 'static
fn y(_: &'static str) {}
// an function that needs a static borrowing
y(baz);
// [!!] Should not compile
// because a static lifetime is required.

The caller gives a parameter with limited lifetime. But the function we get requires a static lifetime parameter. That cannot be allowed. However, let’s consider another example:

/// define a function which takes a function,
/// which takes a parameter with static lifetime.
fn foo(bar: Fn(&'static str) -> ()) {
	bar("Hello Whexy~");
}

let x : Fn(&'static str) -> ();
foo(x); // that makes sense.

let y : Fn(&'a str) -> ();
foo(y); // that makes sense too.

This example is perfect compiled. Because the function y requires a parameter with limited lifetime. The caller gives it a static parameter which lives longer. Again, there should be no concern that the borrowed variable would be unexpectedly dropped. So the contra-variance is a specific rule. T <: U ==> Fn(U) <: Fn(T)

Invariance

Invariance means “no variance”. In a short word, “just pass me exact the thing I require, no tricks.” Let’s see this example:

fn foo(s: &mut &'a str, x: &'a str) {
	*s = x;
}
let mut x = "Hello"; // x : &'static str
let z = String::new();
foo(x, &z); // foo(&'static str, &'z str)
drop(z);
println!("{}", x); // OOPS!

The code cannot compile, because we are going to access x, which points to a dropped memory area. In fact, in &'a mut T, T is invariance. However, the 'a is covariance. It’s not hard to figure out, so I’m left this to you as an exercise.

Variance of types are listed in “The Rustonomicon”

© LICENSED UNDER CC BY-NC-SA 4.0