rust中ref及引用的使用
由C++指针和引用引发的思考
#include<iostream>
using namespace std;
void c1(int a) {
a = 6;
}
void c2(int* a) {
*a = 7;
}
void c3(int& a) {
a = 8;
}
int main() {
int a = 5;
printf("a is %d\n",a);
c1(a);
printf("a is %d\n",a);
c2(&a);
printf("a is %d\n",a);
c3(a);
printf("a is %d\n",a);
}
显然,只有c2和c3函数可以修改a的值,并在主函数生效
等效的rust代码
use std::any::type_name;
//输出变量类型
fn print_type_of<T>(_: &T) {
println!("{}", type_name::<T>());
}
fn c1(mut a:i32) {
a = 5;
println!("c1里面a的类型");
println!("{:p}",&a as *const i32);
print_type_of(&a);
}
fn c2(a:&mut i32) {
*a = 7;
println!("c2里面a的类型");
println!("{:p}",a as *const i32);
print_type_of(&a);
}
//指针,自动从引用转换过来了
fn c4(a:*mut i32) {
unsafe {
*a = 8;
}
println!("c4里面a的类型");
println!("{:p}",a as *const i32);
print_type_of(&a);
}
fn main() {
let mut a = 5;
println!("{:p}",&a as *const i32);
println!("{}",a);
c1(a);
println!("{}",a);
c2(&mut a);
println!("{}",a);
c4(&mut a);
println!("{}",a);
}
输出当然和上面的C++是一样的,但是,rust里面C2和C4函数只是等价于C++C2函数而已。那rust里面有没有和C++引用一样的效果呢,有,但不是完全一样。
再加上一个c3函数
fn c3(ref mut a:i32) {
*a = 100;
println!("c3修改后函数中的a {}",a);
println!("{:p}",a as *const i32);
println!("c3里面a的类型");
print_type_of(&a);
}
fn main() {
//末尾填写如下语句
c3(a);
println!("{}",a);
}
但是,它只是改变了函数里面a的值而已,对main没有变化,虽然,它和C2一样,都是&mut i32类型。
输出如下
0x467ff5f2dc
1
c1里面a的类型
0x467ff5f20c
i32
1
c2里面a的类型
0x467ff5f2dc
&mut i32
7
c4里面a的类型
0x467ff5f2dc
*mut i32
8
c3修改后函数中的a 100
0x467ff5f1a4
c3里面a的类型
&mut i32
8
可见,函数头使用ref,就像是复制了原来的值到新的内存,然后再来一个指针指向新分配的空间
这个问题,可以通过打印指针地址的方式进行查看。(上面已经加上了)
那么,rust里面可不可以使用ref的修改原值呢,当然是可以的。不过rust的ref主要是用于模式匹配。
let mut a = 1;
match a {
ref mut v=> {
*v = 100;//等效于let ref mut v = a;*v=100;
print_type_of(&v);
}
_ =>{},
}
println!("{}",a);
match &mut a {
v => {
*v = 66;
print_type_of(&v);
}
_=>{},
}
println!("{}",a);
print_type_of(&a);
输出如下
&mut i32
100
&mut i32
66
i32
let mut s = Some(10);
match s {
Some(ref mut v) => {
print_type_of(&v);
*v = 100; //为什么这里修改,也有效呢,按照下文就很好理解了。let ref mut v = num; *v=999;num就变成999了。
// println!("{}",v);
}
_ =>{},
}
println!("{:?}",s);
输出如下:
&mut i32
Some(100)
结论:使用match模式匹配时,ref的用法就像C++里面的C3函数那样
再来一些模式匹配中关于ref的例子
#[derive(Debug)]
struct Point {
x:i32,
y:i32,
}
#[derive(Debug)]
enum Mess {
Start {id:i32},
End (i32),
}
fn main() {
let mut p = Point{x:8,y:9};
println!("before {:?}",p);
let Point {x: ref mut x_ref,mut y} = p;
*x_ref = 10;
y = 10; //没有ref,y的修改丝毫不影响原来的p Point
println!("after {:?}",p);
let mut m = Mess::Start{id:9};
//枚举不能这样解构,只能通过if let,或者match匹配
// let Mess::Start{id: ref mut id1} = m;
if let Mess::Start{id: ref mut id1} = m {
*id1 = 200;
}
let mut n = Mess::End(10);
match n {
Mess::End(ref mut id2) => {
*id2 = 66;
}
_ => {},
}
println!("{:?}",m);
println!("{:?}",n);
}
输出如下:
before Point { x: 8, y: 9 }
after Point { x: 10, y: 9 }
Start { id: 200 }
End(66)
再来一些例子
fn main() {
let mut num = 10;
let mut value = Some(&mut num); // Option<&i32> 类型
match value {
Some(ref mut v) => {
**v = 66;
print_type_of(&v);
}
_ => (),
}
println!("{:?}",value);
println!("{}",num);
//这里,为了便于理解match里面匹配的类型。
let ref mut v = &mut num;
print_type_of(&v);
**v = 1000;
println!("{}",num);
}
输出如下:
&mut &mut i32
Some(66)
66
&mut &mut i32
1000
更有意思的是(绕晕了~~)
fn main() {
let mut num = 10;
let mut value = Some(&mut num);
//match1
match value {
Some(ref v) => {
print_type_of(&v);
}
_ => {},
}
//match2
match value {
Some(&mut v) => {
print_type_of(&v);
}
_ => {},
}
//match3
match value {
Some(v) => {
*v = 166;
print_type_of(&v);
}
_ => {},
}
}
/*
输出如下:
&&mut i32
i32
&mut i32
166
*/
如果把match3移到match2前面,编译就不能通过了,原因是Some里面的内容是具有所有权的,match改变了它的所有权。所以,使用ref还是有好处的,可以避免所有权的转移。
这个例子,倒是在闭包参数中很常见,能直接打印变量的实际类型,对学习很有帮助
再来个例子,这次是闭包里面的参数。
// let s = "A man, a plan, a canal: Panama";
//只保留字母数字,并且大写字母变成小写
// let s:String = s.chars().filter(|c| c.is_alphanumeric()).map(|c|c.to_ascii_lowercase()).collect();
let s = "1";
let s:String = s.chars().filter(|c|{
print_type_of(&c);
c.is_alphanumeric()
}).collect();
println!("{}",s);
let s = "2";
let s:String = s.chars().filter(|ref c|{
print_type_of(&c);
c.is_alphanumeric()
}).collect();
println!("{}",s);
let s = "3";
let s:String = s.chars().filter(|&c|{
print_type_of(&c);
c.is_alphanumeric()
}).collect();
println!("{}",s);
/*
输出如下,&char
1
&&char
2
char
3
*/
可见,闭包里面,参数的使用规则,也和match匹配一样。