1所有权
什么是所有权,拥有什么?资源被哪个变量拥有。
1、移动赋值
在c++中,这样的操作是拷贝构造;而在rust中,这样会移动。c++默认情况下给了程序员最大的自由度,而rust正好相反。
let s1 = String::from("hello");
let s2 = s1; // 所有权从s1移动到s2,s1已经是无效的了,不能再使用
对于基本数据类型,整型、浮点型、bool、char等,数据保存到栈上,默认支持Copy语义,赋值操作执行的是拷贝,而不是移动。
let data1 = 10;
let data2 = data1;
println!("data1: {}, data2: {}", data1, data2);
2、调用函数
①值传递,与赋值类似,也是会转移所有权。赋值和值传递,就类似于c++中的unique_ptr,即独占指针,语义是类似的。rust的变量默认都是unique的,并且是const的。
②引用传递,和c++有不一样的地方,c++的函数形参如果接受引用那么就是std::string &str这样的方式,函数调用处的实参不需要使用&来指定,rust中的函数声明以及实参调用的地方都需要使用&来指定是一个引用。否则就会报错。
let s7 = String::from("borrowing");
let len = calculate_length(&s7); // 传递引用,不转移所有权
fn calculate_length(s: &String) -> usize {
s.len()
} // s离开作用域,但因为它没有所有权,所以不会drop任何东西,引用没有所有权
3、引用赋值
引用在rust中也叫借用。
①默认是不可变引用&;可变应用需要加mut,即&mut
②变量本身是mut,即可变的,这个变量才可以被可变引用;如果这个变量不是可变的,那么就不能被可变引用。mut变量可以被不可变引用,也可以被可变引用;非mut变量,只能被不可变引用。
④在同一个作用域,只能有一个可变引用,或者1个或多个不可变引用,可变引用和不可变引用不能同时存在。这个约束类似于linux中的读写锁,在同一时刻,写锁和读锁不能同时加锁;同一时刻,只能有一个写锁;同一时刻,可以有多个读锁。
⑤当不可变引用和可变引用同时存在,或者多个可变引用同时存在时,引用的作用域需要根据实际的代码情况进行分割,代码不能存在自相矛盾的地方。
比如下边的代码,打印r3的话,就说明r3的作用域要持续到println这一行,但是在中间又有个可变引用r4,这就是自相矛盾。如果println这一行打印r4,那么便没有自相矛盾,r3的作用域在r4这一行之前就结束了。
let r3 = &mut s9; // 可变引用 - 在不可变引用作用域结束后允许
let r4 = &mut s9;
println!("r3: {}", r3);
let mut s9 = String::from("rules");
let r1 = &s9; // 不可变引用
let r2 = &s9; // 另一个不可变引用 - 允许
println!("r1: {}, r2: {}", r1, r2);
let r3 = &mut s9; // 可变引用 - 在不可变引用作用域结束后允许
println!("r3: {}", r3);
//println!("r3: {}, r2: {}", r3, r2);//同时使用可变引用和不可变引用
4、可以使用clone方法,类似于c++中的拷贝构造
rust
fn main() {
// 示例1: 所有权转移
println!("=== 所有权转移 ===");
let s1 = String::from("hello");
// s1 = String::from(" world");
let s2 = s1; // 所有权从s1移动到s2
//println!("{}", s1); // 这行会编译错误!s1不再有效
println!("s2: {}", s2);
// 示例2: 克隆(深拷贝)
println!("\n=== 克隆 ===");
let s3 = String::from("world");
let s4 = s3.clone(); // 创建数据的完整副本
println!("s3: {}, s4: {}", s3, s4); // 两者都有效
// 示例3: 函数调用中的所有权
println!("\n=== 函数中的所有权 ===");
let s5 = String::from("rust");
takes_ownership(s5); // s5的所有权移动到函数中
//println!("{}", s5); // 错误!s5不再有效
let s6 = gives_ownership(); // 从函数获取所有权
println!("从函数获取的: {}", s6);
// 示例4: 引用(借用)
println!("\n=== 引用(借用)===");
let s7 = String::from("borrowing");
let len = calculate_length(&s7); // 传递引用,不转移所有权
println!("'{}' 的长度是: {}", s7, len); // s7仍然有效
// 示例5: 可变引用
println!("\n=== 可变引用 ===");
let mut s8 = String::from("hello");
change_string(&s8);
println!("修改后: {}", s8);
// 示例6: 引用规则验证
println!("\n=== 引用规则 ===");
let mut s9 = String::from("rules");
let r1 = &s9; // 不可变引用
let r2 = &s9; // 另一个不可变引用 - 允许
println!("r1: {}, r2: {}", r1, r2);
let r3 = &mut s9; // 可变引用 - 在不可变引用作用域结束后允许
println!("r3: {}", r3);
//println!("r3: {}, r2: {}", r3, r2);//同时使用可变引用和不可变引用
}
fn takes_ownership(some_string: String) {
println!("在函数中: {}", some_string);
} // some_string离开作用域,drop被调用
fn gives_ownership() -> String {
let some_string = String::from("ownership");
some_string // 所有权转移给调用者
}
fn calculate_length(s: &String) -> usize {
s.len()
} // s离开作用域,但因为它没有所有权,所以不会drop任何东西
fn change_string(s: &String) {
//s.push_str(", world!");
}
rust函数声明的格式:
fn 函数名(形参名:形参类型)->返回值类型{}
rust函数返回,只需要把要返回的结果放在最后一行,然后这行没有以;结尾,这就表示返回。如果函数不是在最后一行返回,那么需要使用return关键字。最后一行,使用return也是可以的。
切片:
如下代码,s2就是切片,切片天生就是引用。切片是针对String或者数据的其中一段的引用。
rust
fn main() {
let mut s1 = String::from("hello world");
let s2 = &s1[0..5];
println!("s1 {}, s2 {}",s1,s2);
s1.clear();
}
字符串切片的类型用 &str来表示。
rust
fn main() {
let mut s = String::from("hello world");
let word = first_word(&s);
println!("first word {}", word);
}
fn first_word(s: &str) -> &str {
let bytes = s.as_bytes();
for (i, &item) in bytes.iter().enumerate() {
if item == b' ' {
return &s[0..i];
}
}
&s[..]
}
2泛型
泛型的概念在c++中也有,泛型就是将类型参数化。
泛型的优点是提高代码复用率,相同逻辑、不同参数类型的只需要实现一份就可以。
泛型虽好,但是在实际项目中尽量少用,因为泛型会导致代码可读性变差,维护难度提高,比如使用gdb看到的调用栈函数很长。
函数模板,类模板,结构体模板:概念落脚点是模板,表示一个模板,用来生成具体的函数、类、结构体。
模板函数,模板类,模板结构体:概念落脚点事函数、类、结构体,是模板生成的对象。
2.1函数泛型
在函数名之后,用<>来指定泛型的类型,泛型的参数一般用T来表示。
PartialOrd,要求数据类型是可以比较大小的。
rust
use std::cmp::Ordering;
fn find_max<T: PartialOrd>(a: T, b: T) -> T {
if a > b {
a
} else {
b
}
}
fn main() {
let a = 3;
let b = 5;
println!("{}", find_max(a, b));
let x = 'a';
let y = 'b';
println!("{}", find_max(x, y));
}
c++中的函数模板使用如下:
(1)需要在函数前生成template,而rust是在函数名后的<>中声明参数类型
(2)c++中调用模板函数时,可以在函数名后通过<>指定参数类型(当出现模糊时,必须指定;没有模糊时,则编译器可以自己推导),也可以不指定;rust中都不需要指定,rust也不支持这样的用法。
cpp
#include <iostream>
template<typename T>
T find_max(T a, T b) {
return a > b ? a : b;
}
int main() {
std::cout << find_max(1,2) << std::endl;
std::cout << find_max(1.0,2.0) << std::endl;
std::cout << find_max<int>(1,2.0) << std::endl;
std::cout << find_max('a','b') <<std::endl;
return 0;
}
c++中的函数泛型和rust中的函数泛型有一个很重要的区别:
如下rust的函数模板,虽然只有一个实例,并且是比较u32的大小,没有其它实例,那么编译器也会报错不确定类型T是否支持>操作。rust不如c++灵活。
cpp
use std::cmp::Ordering;
fn find_max<T>(a: T, b: T) -> T {
if a > b {
a
} else {
b
}
}
fn main() {
let a : u32 = 3;
let b : u32 = 5;
println!("{}", find_max(a, b));
//let x = 'a';
//let y = 'b';
//println!("{}", find_max(x, y));
}
而如下c++代码,只有当出现这样的实例的时候,才会进行检查。
cpp
#include <iostream>
template<typename T>
T find_max(T a, T b) {
return a > b ? a : b;
}
class A {
public:
A() {}
};
int main() {
//std::cout << find_max(1,2) << std::endl;
std::cout << find_max(1.0,2.0) << std::endl;
std::cout << find_max<int>(1,2.0) << std::endl;
std::cout << find_max("aaaaa","ccccc") << std::endl;
A a1;
A a2;
std::cout << find_max(a1, a2) << std::endl;
return 0;
}
2.2结构体泛型
结构体泛型的声明方式与函数类似,均是在名字后边使用<>来声明。
rust
struct Point<T> {
x: T,
y: T,
}
fn main() {
let integer = Point{x:5,y:6};
let float = Point{x:1.0,y:2.0};
}
c++中的结构体模板:
cpp
#include <iostream>
template <typename T>
struct Point {
T x;
T y;
};
int main() {
Point<int> p1;
Point<double> p2;
p1.x = 1;
p1.y = 2;
p2.x = 1.0;
p2.y = 2.0;
return 0;
}
在c++中使用结构体模板,声明时,必须指定类型;在rust中,不需要这样指定。
3.3枚举泛型
rust枚举泛型如下所示。
rust
#[derive(Debug)]
enum MyOption<T> {
Some(T),
None,
}
#[derive(Debug)]
enum MyResult<T,E> {
Ok(T),
Err(E),
}
fn main() {
let some_number : MyOption<i32> = MyOption::Some(5);
let no_number : MyOption<i32> = MyOption::None;
let success: MyResult<String, &str> = MyResult::Ok("操作成功".to_string());
let fail: MyResult<String, &str> = MyResult::Err("出错了");
println!("{:?},{:#?}", success, fail);
}
c++中的枚举不支持泛型。
2.4c++中泛型的概念
2.4.1c++类模板为什么不能编译到动态库中来使用
c++类模板为什么不能编译到动态库中来使用_c++模板类变成动态库-CSDN博客
2.4.2代码膨胀现象
对于一个模板来说,编译出的目标文件中,最终有多少个这个模板的实例?
如下代码,虽然很少这样使用,但会产生5个实例。
cpp
#include <iostream>
template<typename T, int num>
class A {
public:
A() {}
void run(T data) {
std::cout << "data:" << data << ",num:" << num << std::endl;
}
};
int main() {
A<int, 1> o1;
A<int, 2> o2;
A<int, 3> o3;
A<int, 4> o4;
A<int, 5> o5;
o1.run(1);
o2.run(2);
o3.run(3);
o4.run(4);
o5.run(5);
return 0;
}
通过objdump查看符号,可以看到类A的构造函数和run函数均有5个。

如下代码,1个实例:
cpp
#include <iostream>
template<typename T, int num = 10>
class A {
public:
A() {}
void run(T data) {
std::cout << "data:" << data << std::endl;
}
};
int main() {
A<int> o1;
A<int> o2;
A<int> o3;
A<int> o4;
A<int> o5;
o1.run(1);
o2.run(2);
o3.run(3);
o4.run(4);
o5.run(5);
return 0;
}
下边的代码,4个实例:
cpp
#include <iostream>
template<typename T, int num = 10>
class A {
public:
A() {}
void run(T data) {
std::cout << "data:" << data << std::endl;
}
};
int main() {
A<int, 1> o1;
A<int, 1> o2;
A<int, 2> o3;
A<int, 3> o4;
A<int, 4> o5;
o1.run(1);
o2.run(2);
o3.run(3);
o4.run(4);
o5.run(5);
return 0;
}
2.4.3模板的特化
本来模板就是为了减少代码,但是又出现了模板特化,模板特化跟我们不是用模板,直接写几个重载函数是类似的。本来是一种进步的技术,但是也不能完全把旧的技术代替。类似于引用,c++中的引用是为了简化指针的使用,但是现在随着左值引用,右值引用,万能引用概念的出现,个人认为引用的复杂度要高于c语言中的指针。类似于这样的矛盾的现象,还有很多。
模板的特化分为全部特化和部分特化:全部特化,模板的所有参数的类型都指定;部分特化是指部分模板参数特化。
重载函数 > 全部特化模板 > 部分特化模板 > 非特化模板。
(1)被特化的参数,不能再出现在template列表中
(2)函数模板特化,直接在函数的形参中确定类型就可以;类模板的特化,需要在类名的后边使用<>指定类型
下边的代码,main函数的第一行调用会打印overloading,第二行调用会打印part float instance。
cpp
#include <iostream>
bool isEqual(int a, int b, char c, char d) {
std::cout << "overloading" << std::endl;
return ((a == b) && (c == d));
}
template <typename T, typename U>
bool isEqual(T a, T b, U c, U d) {
std::cout << "template" << std::endl;
return ((a == b) && (c == d));
}
template <typename T>
bool isEqual(T a, T b, char c, char d) {
std::cout << "part char instance" << std::endl;
return ((a == b) && (c == d));
}
template <>
bool isEqual(int a, int b, char c, char d) {
std::cout << "all instance" << std::endl;
return ((a == b) && (c == d));
}
template <typename T>
bool isEqual(T a, T b, float c, float d) {
std::cout << "part float instance" << std::endl;
return ((a == b) && (c == d));
}
int main() {
isEqual(1,1,'a','a');
isEqual(1,1,1.0f,1.0f);
return 0;
}
3trait
rust中的trait类似于c++中的虚函数。trait可以有默认实现,也可以没有默认实现,类似于c++中的虚函数和纯虚函数。trait和虚函数用来定义接口,规范接口,是实现多态的基础。
c++中的虚函数实现多态,通过继承的方式来实现;rust中trait实现多态,类似于组合的方式,更加分散。
3.1example
(1)trait xxx声明一个trait,一个trait中可以声明一个或者多个方法
(2)trait中的方法可以有默认实现,也可以没有默认实现;没有默认实现的trait,那么trait实现中必须得实现
(3)impl xxxtrait for xxx
rust
pub trait Summary {
fn summarize_author(&self) -> String;
fn summarize(&self) -> String {
format!("Read more from {}...", self.summarize_author())
}
}
pub struct NewsArticle {
pub headline : String,
pub location : String,
pub author : String,
pub content : String
}
pub struct Tweet {
pub username : String,
pub content : String,
pub author : String,
pub reply : bool,
pub retweet : bool
}
impl Summary for NewsArticle {
fn summarize_author(&self) -> String {
format!("{}",self.author)
}
}
impl Summary for Tweet {
fn summarize_author(&self) -> String {
format!("{}",self.author)
}
fn summarize(&self) -> String {
format!("summarize of Tweet {}", self.summarize_author())
}
}
fn main() {
let news = NewsArticle {
headline : String::from("news---headline"),
location : String::from("news-location"),
author : String::from("news-author"),
content : String::from("news-content")
};
println!("news article: {}", news.summarize());
let tweet = Tweet {
username : String::from("tweet-username"),
content : String::from("tweet-content"),
author : String::from("tweet-author"),
reply : false,
retweet : false
};
println!("tweet: {}", tweet.summarize());
}
3.2trait约束
在泛型中约束类型。
rust
fn largest<T: PartialOrd + Copy>(list : &[T]) -> T {
let mut largest = list[0];
for &item in list.iter() {
if item > largest {
largest = item;
}
}
largest
}
fn main() {
let number_list = vec![1,2,3,4,5];
let result = largest(&number_list);
println!("largest number {}", result);
let char_list = vec!['a','b','c','d','e'];
let result = largest(&char_list);
println!("largest char {}", result);
}
3.3trait参数和返回值
report的形参是trait,make_news_article的返回值是trait。
rust
pub trait Summary {
fn summarize_author(&self) -> String;
fn summarize(&self) -> String {
format!("Read more from {}...", self.summarize_author())
}
}
pub struct NewsArticle {
pub headline : String,
pub location : String,
pub author : String,
pub content : String
}
pub struct Tweet {
pub username : String,
pub content : String,
pub author : String,
pub reply : bool,
pub retweet : bool
}
impl Summary for NewsArticle {
fn summarize_author(&self) -> String {
format!("{}",self.author)
}
}
impl Summary for Tweet {
fn summarize_author(&self) -> String {
format!("{}",self.author)
}
fn summarize(&self) -> String {
format!("summarize of Tweet {}", self.summarize_author())
}
}
fn report(item : impl Summary) {
println!("report {}", item.summarize());
}
fn make_news_article() -> impl Summary {
NewsArticle {
headline : String::from("news---headline1"),
location : String::from("news-location1"),
author : String::from("news-author1"),
content : String::from("news-content1")
}
}
fn main() {
let news = NewsArticle {
headline : String::from("news---headline"),
location : String::from("news-location"),
author : String::from("news-author"),
content : String::from("news-content")
};
println!("news article: {}", news.summarize());
let tweet = Tweet {
username : String::from("tweet-username"),
content : String::from("tweet-content"),
author : String::from("tweet-author"),
reply : false,
retweet : false
};
println!("tweet: {}", tweet.summarize());
report(tweet);
let news1 = make_news_article();
report(news1);
}
4线程
4.1线程
类似于c++,创建线程可以直接传入已经定义的函数,也可以传入闭包,rust中的闭包和c++中的lambda表达式类似。c++中的lambda表达是可以捕获数据,引用捕获和值捕获都可以;rust中的闭包,需要通过move。
本例子中,handle2如果只用到msg的话,不需要move捕获,为什么?
rust
use std::thread;
use std::time::Duration;
fn hello_thread() {
println!("hello thread");
}
fn print_message(msg: String) {
println!("msg: {}", msg);
}
fn main() {
let handle1 = thread::spawn(hello_thread);
let handle2 = thread::spawn(|| {
for i in 1..10 {
println!("hi number {} from this spawned thread2!", i);
thread::sleep(Duration::from_millis(1));
}
}
);
let msg = String::from("hello rust thread");
let v = vec![1, 2, 3];
let handle3 = thread::spawn(move || {
print_message(msg);
println!("Here's a vector: {:?}", v);
}
);
for i in 1..5 {
println!("hi number {} from the main thread!", i);
thread::sleep(Duration::from_millis(1));
}
handle1.join().unwrap();
handle2.join().unwrap();
handle3.join().unwrap();
}
4.2通道channel
类似于go语言,go语言中也有channel。
rust
use std::thread;
use std::sync::mpsc;
use std::time::Duration;
fn main() {
let (tx, rx) = mpsc::channel();
let tx1 = mpsc::Sender::clone(&tx);
let handle1 = thread::spawn(move || {
let vals = vec![String::from("hello"), String::from("thread1")];
for val in vals {
tx.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
}
});
let handle2 = thread::spawn(move || {
let vals = vec![String::from("hello"), String::from("thread2")];
for val in vals {
tx1.send(val).unwrap();
thread::sleep(Duration::from_secs(1));
}
});
for recv in rx {
println!("recv {}", recv);
}
handle1.join().unwrap();
handle2.join().unwrap();
}
4.3多线程加锁
c++中的锁和数据是分离的,而rust中的锁和数据是集成到一块的。
rust
use std::sync::{Mutex, Arc};
use std::thread;
fn main() {
let counter = Arc::new(Mutex::new(0));
let mut handles = vec![];
for _ in 0..10 {
let counter = Arc::clone(&counter);
let handle = thread::spawn(move || {
let mut num = counter.lock().unwrap();
*num += 1;
});
handles.push(handle);
}
for handle in handles {
handle.join().unwrap();
}
println!("result {}", *counter.lock().unwrap());
}