rust入门

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());
}
相关推荐
无心水1 小时前
2、Go语言源码文件组织与命令源码文件实战指南
开发语言·人工智能·后端·机器学习·golang·go·gopath
2501_944526421 小时前
Flutter for OpenHarmony 万能游戏库App实战 - 主题切换实现
android·开发语言·javascript·python·flutter·游戏·django
张张努力变强1 小时前
C++ 类和对象(三):拷贝构造函数与赋值运算符重载之核心实现
开发语言·c++
福楠2 小时前
C++ STL | set、multiset
c语言·开发语言·数据结构·c++·算法
一晌小贪欢2 小时前
Python 健壮性进阶:精通 TCP/IP 网络编程与 requirements.txt 的最佳实践
开发语言·网络·python·网络协议·tcp/ip·python基础·python小白
enfpZZ小狗2 小时前
基于C++的反射机制探索
开发语言·c++·算法
曹牧2 小时前
C#:WebReference
开发语言·c#
黎雁·泠崖2 小时前
Java static入门:概述+静态变量特点与基础实战
java·开发语言
玉梅小洋2 小时前
C盘爆满 修改VS Code缓存与插件目录指定方法
开发语言·windows·visualstudio