Rust 进阶学习

Rust 进阶学习

文章目录

  • Rust 进阶学习
    • 所有权
      • 作用域
      • 移动和克隆
      • 涉及函数的所有权机制
        • 涉及参数的所有权
        • 涉及返回值的所有权
      • 引用和租借
      • 可变引用
    • 枚举类
      • 枚举成员的属性
      • 枚举匹配
    • 结构体
      • 结构体方法
      • 结构体关联函数
    • 错误处理
      • 不可恢复错误
      • 可恢复错误
    • Rust代码组织管理
      • Module
          • 默认的Module
          • 引用Module
      • Crate
      • Package
    • Rust面向对象
      • trait(特性)
      • 默认特性
      • 小结

所有权

所有权规则:

  • 每个值都有一个变量,这个变量称为所有者;
  • 每个值同时只能有一个所有者;
  • 当所有者超出作用域时,它在内存中对应的值会被删除。

作用域

最简单的就是函数内的变量,在函数体外无法使用,因为在函数体外变量已经被删除

fn demo_func(){let demo_var = 1;println!("{}", demo_var);//函数体内的变量可以使用
}
fn main() {demo_func();println!("demo_var: {}", demo_var);//超出作用域无法使用
}

移动和克隆

移动需要分两种情况讨论。

对于基础数据类型以及由基础数据类型构成的复合类型,都是保存在栈内存中的数据值,进行变量赋值时,会在内存中复制一个同样的值。

如:

fn main() {let x = 5;let y = x;println!("x: {}, y: {}", x, y);/* x: 5, y: 5 */
}

对于保存在堆中的值,变量赋值时,会进行“移动”。

如,对String变量进行变量间的赋值:

fn main() {let s1 = String::from("hello");let s2 = s1; println!("s2: {}", s2); // s2通过移动指向了字符串hello的内存,可以打印println!("s1: {}", s1); // s1 已经失效,编译报错。 
}

s1赋值给s2后,因为任何值只有一个所有者,s1不能再指向hello字符串这个内存值,所以s1不再有效。即进行了所谓的“移动”。

如果想要实现变量值之间的复制,需用通过克隆。如:

fn main() {let s1 = String::from("hello");let s2 = s1.clone(); // 将hello字符串克隆一份,s2指向克隆后的内存println!("s2: {}", s2); // s2指向克隆后的hello字符串println!("s1: {}", s1);
}

通过克隆,在内存中生成了一份相同的值,让新的变量指向它,不会导致原有内存值所有权被剥夺。

涉及函数的所有权机制

涉及参数的所有权

对于堆中的变量值,所有权会跟随函数参数进行转移:

fn main() {let str = String::from("param");string_print(str);// 此处str已经无效,param字符串的所有权已经被转移到函数中print!("str: {}", str);
}fn string_print(str: String) {println!("{}", str);
}

基础数据类型变量值不会转移:

fn main() {let number = 10;number_print(number);println!("number: {}", number);
}
/*  输出:
number_print: 10
number: 10
*/fn number_print(num: i32) {println!("number_print: {}", num);
}

涉及返回值的所有权

当变量被函数当作返回值返回时,所有权也会转移:

fn main() {let s1 = return_string();println!("s1: {}", s1);
}fn return_string() -> String {let str = String::from("a string");return str;
}

来看一下更复杂的情况,返回函数参数:

fn main() {let s1 = return_string();println!("s1: {}", s1);// 字符串hello所有权从函数中移动到s1let return_str = return_string_from_param(s1);// 字符串hello所有权移入函数中,再从函数中移出// println!("s1: {}", s1);// 此处s1已经无效,之前的内存值已经其他所有者占用println!("return_str: {}", return_str);}fn return_string() -> String {let str = String::from("hello");return str;
}fn return_string_from_param(str: String) -> String {return str;
}

还是上面的原则,所有权会随函数参数移动,函数内的值也会随返回值移动。

引用和租借

引用不会占用所有权,通过引用可以访问变量值,但不能修改值。

fn main() {let str = String::from("hello");let str_reference = &str;//引用strprintln!("str: {}, str_reference: {}", str, str_reference);// str: hello, str_reference: hello// str_reference.push_str("world");// 报错,引用无法修改原有的值
}

引用可以理解为类似c指针指向。引用变量会指向变量本身,但变量值的所有权还是归原变量所有。通过引用访问变量值,是对所有权的“租借”。“租借”只能进行值访问,不能进行值修改。

引用指向的是变量本身,当变量失去内存值的所有权,导致无效时,引用也同样无效。如:

fn main() {let str = String::from("hello");// str获得hello字符串的所有权let str_reference = &str;//引用strlet str_replace = str;// hello字符串所有权被移动,str无效println!("str_replace: {}", str_replace);// println!("str_reference: {}", str_reference);//报错,引用指向的是str,str已经无效,引用也无效
}

最后一行关闭注释,编译时会报错,提示你变量已经被租借,无法再移动:

boys@server:~/rust_study/demo$ cargo runCompiling demo v0.1.0 (/home/boys/rust_study/demo)
error[E0505]: cannot move out of `str` because it is borrowed--> src/main.rs:4:23|
2 |     let str = String::from("hello");// str获得hello字符串的所有权|         --- binding `str` declared here
3 |     let str_reference = &str;//引用str|                         ---- borrow of `str` occurs here
4 |     let str_replace = str;// hello字符串所有权被移动,str无效|                       ^^^ move out of `str` occurs here
5 |     println!("str_replace: {}", str_replace);
6 |     println!("str_reference: {}", str_reference);//报错,引用指向的是str,str已经无效,引用也无效|                                   ------------- borrow later used hereFor more information about this error, try `rustc --explain E0505`.
error: could not compile `demo` (bin "demo") due to previous error

可变引用

普通引用无法修改原有内存值,通过可变引用可以修改。需要加关键字 mut

fn main() {let mut str = String::from("hello ");// str是mut可变的let str_reference = &mut str;//可变引用strstr_reference.push_str("world");println!("str_reference: {}", str_reference);//str_reference: hello world
}

为了防止出现并发访问问题,可变引用不可以多重引用。如:

fn main() {let mut string = String::from("hello");let s1 = &mut string;let s2 = &mut string;println!("s1: {}, s2: {}", s1, s2);//此处打印会报错,不能进行多重可变引用
}

会提示:

cannot borrow `string` as mutable more than once at a time

不可变引用因为无法修改变量的内存值,是只读的,不会产生竞态,因此可以多重引用。

fn main() {let string = String::from("hello");let s1 = &string;let s2 = &string;println!("s1: {}, s2: {}", s1, s2);// 不可变引用因为只是读取了变量内存值,可以进行多重引用
}

可变引用和不可变引用不能同时使用。非常容易理解,因为可变引用可以修改内存值,这会导致竞态、并发访问问题。RUST中是不允许的。

枚举类

枚举是对某类事物可能情况的列举。使用enum关键字定义枚举。

如,枚举操作系统类型:

enum OperateSystem{Android,IOS,Linux,Unix,Windows,
}

打印枚举,需要添加宏:

#[derive(Debug)]

使用 {:?}占位符格式化为字符串打印出来:

#[derive(Debug)]
enum OperateSystem{Android,IOS,Linux,Unix,Windows,
}
fn main() {let os = OperateSystem::Android;println!("the os is: {:?}", os);//the os is: Android
}

枚举成员的属性

rust中的枚举可以包含属性,属性可以是不同类型。如:

#[derive(Debug)]
enum Shape{Circle{ radius: f64},Rectangle{ width: f32, length: f32},
}
fn main() {let circle = Shape::Circle { radius: 3.0};println!("circle: {:?}", circle);let rectangle = Shape::Rectangle { width: 3.0, length: 4.0 };println!("rectangle: {:?}", rectangle);/* 打印输出: circle: Circle { radius: 3.0 }rectangle: Rectangle { width: 3.0, length: 4.0 }*/
}

上面代码定义了一个Shape形状枚举,有两个成员,分别是圆形和矩形。

圆形有一个半径(radius)属性,使用f64类型;

矩形有两个属性,分别是长度(length)和宽度(width),都使用f32类型。

枚举类成员的属性也可以是匿名的,如:

#[derive(Debug)]
enum Shape{Circle(f64),Rectangle(f32, f32),
}
fn main() {let circle = Shape::Circle(3.0);println!("circle: {:?}", circle);let rectangle = Shape::Rectangle(3.0, 4.0);println!("rectangle: {:?}", rectangle);/* 打印输出: circle: Circle(3.0)rectangle: Rectangle(3.0, 4.0)*/
}

枚举匹配

使用match语法进行匹配:

#[derive(Debug)]
enum Shape{Circle{ radius: f64},Rectangle{ width: f32, length: f32},
}
fn main() {let circle = Shape::Circle { radius: 3.0};let rectangle = Shape::Rectangle { width: 3.0, length: 4.0 };match circle {Shape::Circle { radius } => println!("circle radius: {}", radius),Shape::Rectangle { width, length } => println!("rectangle width: {}, length: {}", width, length)}
}

匿名属性也可以在match语句中临时设置一个参数名,如:

#[derive(Debug)]
enum Shape{Circle( f64 ),Rectangle( f32, f32),
}
fn main() {let circle = Shape::Circle(3.0);match circle {Shape::Circle(radius) => {println!("circle radius: {}", radius)},Shape::Rectangle(width, length) => {println!("rectangle width: {}, length: {}", width, length)},}
}

结构体

结构体方法

结构体方法的形式与函数一致,之所以叫结构体方法,是因为它的第一个参数是结构体本身的引用。

直接上例子:

struct Rectangle {width: f32,length: f32
}impl Rectangle {/* 使用impl为结构体添加方法 */fn get_area(&self) -> f32 {// 第1个参数是对结构体本身的引用return self.width*self.length}
}
fn main() {let rectangle = Rectangle { width: 3.0, length: 4.0 };let area = rectangle.get_area();println!("rectangle's area: {}", area);
}

&self参数只是在声明时需要添加,调用时不需要传入。

结构体关联函数

结构体关联函数不依赖于结构体实例。

与结构体方法的直观区别是,方法没有&self作为第1个参数的要求。

struct Rectangle {width: f32,length: f32
}impl Rectangle {/* 使用impl为结构体添加方法 */fn create_instance(width_set: f32, length_set: f32) -> Rectangle {Rectangle { width: width_set, length: length_set }}fn get_area(&self) -> f32 {// 第1个参数是对结构体本身的引用return self.width*self.length}
}
fn main() {let rectangle = Rectangle::create_instance(5.0, 6.0);let area = rectangle.get_area();println!("rectangle's area: {}", area);
}

错误处理

不可恢复错误

Rust中的不可恢复错误(Unrecoverable Error)指的是发生了无法正常处理的错误,比如数组越界、空指针解引用等。当程序遇到不可恢复错误时,会触发panic(恐慌)并终止程序的运行。

可以使用panic宏主动触发panic,如:

fn main() {panic!("just a test panic!");println!("can not run here.");
}

运行时会主动触发pianic来终止程序运行:

boys@server:~/rust_study/demo$ ./target/debug/demo 
thread 'main' panicked at 'just a test panic!', src/main.rs:2:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

根据提示,加入RUST_BACKTRACE=1环境变量运行,可以追踪到panic发生的地方:

boys@server:~/rust_study/demo$ RUST_BACKTRACE=1 ./target/debug/demo 
thread 'main' panicked at 'just a test panic!', src/main.rs:2:5
stack backtrace:0: rust_begin_unwindat /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/std/src/panicking.rs:578:51: core::panicking::panic_fmtat /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/panicking.rs:67:142: demo::mainat ./src/main.rs:2:53: core::ops::function::FnOnce::call_onceat /rustc/90c541806f23a127002de5b4038be731ba1458ca/library/core/src/ops/function.rs:250:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

可恢复错误

可恢复错误指的是那些在运行时可以被程序处理和修复的错误,这些错误通常是可以预见和处理的。具体而言,可恢复错误可以包括但不限于以下情况:

  1. 文件操作错误 :如文件不存在、无法打开文件、读取或写入文件失败等。
  2. 网络通信错误 :如连接超时、服务器错误、网络断开等。
  3. 输入验证错误 :如用户输入不合法、缺少必要参数等。
  4. 数据库操作错误 :如查询失败、连接错误、事务回滚等。
  5. 外部资源错误 :如磁盘空间不足、内存耗尽等。
  6. 解析和转换错误 :如JSON解析错误、数据类型转换失败等。
  7. 并发操作错误 :如竞争条件、死锁等。
  8. 配置和环境错误 :如配置文件损坏、依赖缺失等。

可恢复错误与不可恢复错误(例如硬件故障、内存错误、空指针引用等)不同,后者通常无法在运行时被程序处理和修复,需要采取其他措施(如程序重启、报告错误给用户等)。

对于可恢复错误,使用Result类型可以提供一种良好的错误处理机制,使得程序能够检测到错误并根据需要采取适当的处理措施。如:

use std::{fs::File, error};fn main() {let fp = File::open("./file.txt");match fp {Result::Ok(file) => {println!("open {:?} success", file);},Result::Err(error) => {println!("open file fail: {}", error);}}
}

没有文件时会输出:

boys@server:~/rust_study/demo/target/debug$ ./demo 
open file fail: No such file or directory (os error 2)

Rust代码组织管理

Rust 代码组织中有三个重要的概念:Crate(箱)、Package(包)、Module(模块)

Module

Module(模块)类似于命名空间,用来管理代码的作用域。

使用mod关键字来定义一个Module,引用时使用符号 ::

下面是一个示例:

mod utility {pub fn print_num(num: i32){println!("{}", num);}
}fn main() {utility::print_num(33);
}

Module内的成员是有访问权限控制的,默认的访问权限是在Module内,要在Module外能引用,需要加入pub关键字声明,表明这个成员是public的。如:

mod utility {fn debug_print(num: i32) {println!("num = {}, hex: {:#x}", num, num);}pub fn print_num(num: i32){println!("{}", num);debug_print(num);// 只有内部才能使用}
}fn main() {utility::print_num(33);// utility::debug_print(33);/* module外使用会报错 */
}

Module内可以定义其他的Module,形成层次。如:

mod utility {pub mod printer{pub fn println(num: i32){println!("{}", num);}}pub mod calculator{pub fn get_numbers_sum(num1: i32, num2: i32) -> i32{num1+num2}}
}fn main() {let sum = utility::calculator::get_numbers_sum(1, 1);utility::printer::println(sum);
}
默认的Module

创建.rs文件时,就会产生一个与文件名同名的Module。如:

在ulitity.rs中代码如下:

pub mod printer{pub fn println(num: i32){println!("{}", num);}
}
pub mod calculator{pub fn get_numbers_sum(num1: i32, num2: i32) -> i32{num1+num2}
}

虽然我们没有使用mod关键字声明一个Module来包含这两个module,但默认已经使用了文件名作为默认的Module。

在main.rs中声明一下,即可调用:

mod utility;/* 声明Module */
fn main() {let sum = utility::calculator::get_numbers_sum(1, 1);utility::printer::println(sum);
}
引用Module

使用use关键字对一个Module或Module内的成员进行引用。如:

mod utility {pub mod printer{pub fn println(num: i32){println!("{}", num);}}pub mod calculator{pub fn get_numbers_sum(num1: i32, num2: i32) -> i32{num1+num2}}
}use utility::printer;/* 引用printer module */
use utility::calculator::get_numbers_sum;/* 引用calculator内的成员函数: get_numbers_sum */
fn main() {let sum = get_numbers_sum(1, 1);/* 使用时不用再注明前面的module */printer::println(sum);
}

Crate

Crate 是 Rust 的构建块,用于组织和管理代码,它可以包含一个或多个Module(模块)和其他项(如函数、结构体、枚举等)。

Crate可以分为2种类型:

  • lib,即库类型Crate
  • bin,即二进制Crate

库Crate可以生成库文件,给其他代码共享、复用,二进制Crate生成的是可执行文件。

通常情况下,Rust 项目的 Crate 遵循一种约定俗成的目录结构,有助于组织和管理代码。

以下是一个常见的 Rust 项目目录结构示例:

my_project/
├── src/             # 存放源代码
│   ├── main.rs      # 二进制 Crate 的入口文件
│   └── lib.rs       # 库 Crate 的入口文件
├── tests/           # 存放测试代码
├── examples/        # 存放示例代码
├── Cargo.toml       # 描述包和依赖关系的文件
└── README.md        # 项目文档

Package

Package(包)包含了Crate,可以同时包含多个Crate。但多个Crate里面只能有一个库Crate,可以有多个二进制Create。

Rust面向对象

trait(特性)

trait(特性)简单地说,就是一组通用的属性,当需要使用这些特性时,只需要通过impl关键字实现他们,即可拥有这些特性。

如:

trait Person{/* 定义一个关于Person的特性 */fn get_name(&self) -> &str;/* 获取姓名的接口 */
}

上面定义了一个Person的特性,特性包含一个获取姓名的接口。当需要获得这个特性时,即可通过impl关键字实现,如:

trait Person{/* 定义一个关于Person的特性 */fn get_name(&self) -> &str;/* 获取姓名的接口 */
}
struct XiaoMing{name: String,
}
struct ZhangSan{name: String
}impl Person for XiaoMing {/* 为xiaoming实现Person特性 */fn get_name(&self) -> &str {&self.name}
}
impl Person for ZhangSan {/* 为zhangsan实现Person特性 */fn get_name(&self) -> &str {&self.name}
}fn main() {let ming = XiaoMing{name: String::from("mingming"),};println!("xiaoming's name: {}", ming.get_name());let zhang = ZhangSan{name: String::from("zhangsan")};println!("zhang_san's name: {}", zhang.get_name());
}

默认特性

trait可以实现定义的接口,如果实现这个特性的类型没有对该接口覆盖,则调用这个接口时还是调用trait中默认接口。如:

trait Person{/* 定义一个关于Person的特性 */fn get_name(&self) -> &str;/* 获取姓名的接口 */fn tell_age(&self){/* 默认实现,未实现会调用此默认接口 */println!("unkown age");}
}
struct XiaoMing{name: String,
}
struct ZhangSan{name: String
}impl Person for XiaoMing {/* 为xiaoming实现Person特性 */fn get_name(&self) -> &str {&self.name}fn tell_age(&self){println!("xiaoming is 18 years old");}
}
impl Person for ZhangSan {/* 为zhangsan实现Person特性 */fn get_name(&self) -> &str {&self.name}
}fn main() {let ming = XiaoMing{name: String::from("mingming"),};ming.tell_age();let zhang = ZhangSan{name: String::from("zhangsan")};zhang.tell_age();/* 输出结果:xiaoming is 18 years oldunkown age*/
}

小结

train(特性)有点类似cpp中的虚函数和纯虚函数,没有实现的接口,在impl时必须实现。有默认实现的,如果impl未实现,则会调用默认的实现。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/65997.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

JavaScript原型链污染

前言 在浏览某个论坛的时候,第一次看到了JavaScript原型链污染漏洞。当时非常的好奇,当时我一直以为js作为一种前端语言,就算存在漏洞也是针对前端,不会危害到后端,因此我以为这种漏洞危害应该不大。可当我看到他的漏…

简明易懂:Python中的分支与循环

文章目录 前言分支结构if 语句:单一条件判断else语句:提供备选方案elif 语句:多条件判断嵌套的分支结构:复杂条件逻辑 循环结构for循环:遍历序列range()函数与for循环while循环:条件重复循环控制&#xff1…

PAT编程基础笔记

写在前面&#xff1a; 这篇文章是我之前刷PAT(Basic Level)随手记下来的几个记忆点&#xff0c;希望对大家有所帮助。 1.字符串和数值类型之间的转换&#xff1a; 如果要将数值类型如int、double、long转换为字符串string&#xff0c;可以使用to_string()函数 #include<s…

GA遗传算法

储备知识 GA算法主要解决数学模型中最优化的搜索算法&#xff0c;是进化算法中的一种&#xff0c;基因算法借鉴了自然界基因的遗传的主要现象&#xff0c;分别为遗传&#xff0c;变异&#xff0c;自然选择&#xff0c;杂交等。 GA算法参数 GA算法的参数如下所示。 种群规模…

剑指 Offer 62. 圆圈中最后剩下的数字(简单)

题目&#xff1a; class Solution { public:int lastRemaining(int n, int m) {int pos 0;for(int i2;i<n;i){pos (posm)%i;}return pos;} };作者&#xff1a;想吃火锅的木易 链接&#xff1a;详细题解 来源&#xff1a;力扣&#xff08;LeetCode&#xff09;

基于python的改进高斯混合模型的图割算法研究设计与实现

摘要 信息技术永远是改变生活的第一种创新方式&#xff0c;各种行业的发展更是脱离不了科技化的支持。原本传统的行业正在被科技行业的切入悄悄的发生变化。就拿我们生活当中常见的事情举例而言&#xff0c;在外卖行业还没有发展的快速的时候&#xff0c;方便面等速食成了快速吃…

ssh常用操作

ssh常用操作 SSH是一种安全协议&#xff0c;ssh是该协议的客户端程序&#xff0c;openssh-server则是该协议的服务端程序 常用系统都自带了ssh客户端程序&#xff0c;服务端程序则可能要安装 密码远程登陆 前提&#xff1a;服务器安装了openssh-server&#xff0c;未安装时…

安装bpftrace和bcc的踩坑记录

最后在Ubuntu22.04使用Ubuntu提供的安装命令完成了安装。这里是记录尝试在Ubuntu18.04和Ubuntu22.04使用源码安装未果的过程。 文章目录 22版本安装bcc准备工具安装命令使用报错&#xff1a;iovisor封装的安装方式ubuntu的安装方式 For Bionic (18.04 LTS)官方提供的源码安装准…

Tutorial: Mathmatical Derivation of Backpropagation

目录 1. 概要 2. Gradient Descent 3. Chain rule 3.1 单变量基本链式法则 3.2 单变量全微分链式法则 3.3 小贴士&#xff1a;微分、导数、导函数是什么关系&#xff1f; 4. What and why backpropagation? 5. Backpropagation for a simple neural network 5.1 基于…

哪些存储设备的数据需要注意,防止误删除或者格式化丢失?

以下是一些存储设备的数据要注意&#xff0c;防止误删除或者格式化丢失&#xff1a; 1.硬盘&#xff1a;存储重要数据时要备份&#xff0c;避免硬盘故障、误格式化等情况导致数据丢失。 2.USB闪存驱动器&#xff1a;在拔出USB闪存驱动器前&#xff0c;应该先进行“安全删除”…

Jmeter(三十一):制造大批量的用户数据数据

需求&#xff1a;因测试需要&#xff0c;要造100w用户数据&#xff0c;通过用户名、手机号、密码可新增用户&#xff0c;其中用户名和电话号码要求100w用户不能重复 要点&#xff1a; 1、通过Bean shell Sampler实现用户名和手机号的足够随机。 符合我们常用规则的手机号&#…

element侧边栏子路由点击不高亮问题

最近自己封装侧边栏 又碰到了点击子路由不高亮的问题 <template><div class"aside"><el-scrollbar :vertical"true" class"scrollbar_left_nav"><el-menu :default-active"defaultActive" :collapse"$stor…

关于内网主备+https

先openssl证书 整体流程为&#xff1a; 1、页面访问为https&#xff0c;在电脑修改hosts文件&#xff0c;如域名为 babaozhou.com, 则配置为 ip1 babaozhou.com&#xff0c;ip2 babaozhou.com&#xff1b; 也就是说同域名关联两个ip&#xff0c;这样如果服务器1ping不通了则可…

CTFhub-文件上传-.htaccess

首先上传 .htaccess 的文件 .htaccess SetHandler application/x-httpd-php 这段内容的作用是使所有的文件都会被解析为php文件 然后上传1.jpg 的文件 内容为一句话木马 1.jpg <?php echo "PHP Loaded"; eval($_POST[a]); ?> 用蚁剑连接 http://ch…

遗传算法决策变量降维的matlab实现

1.案例背景 1.1遗传算法概述 遗传算法是模拟达尔文生物进化论的自然选择和遗传学机理的生物进化过程的计算模型,是一种通过模拟自然进化过程搜索最优解的方法。它最初由美国Michigan大学的J. Holland教授提出,1967年, Holland 教授的学生 Bagley在其博士论文中首次提出了“遗传…

利用frps搭建本地自签名https服务的透传

nginx的搭建就不介绍了&#xff0c;教程很多&#xff0c;基本上油手就会。 在本例中&#xff0c;frp服务器的域名是 www.yourfrp.com&#xff0c;同时也是反向代理nginx服务器; 本地网站要用的域名&#xff1a; test.abcd.com 请事先将 test.abcd.com 解析到 frp所在服务器…

CC1310开发工具下载及环境搭建

目录 CC1310开发工具集 CC1310SDK下载安装注意事项 CCS下载安装注意事项 导入示例代码 CCS常用设置 CC1310是TI的一款sub1G射频模块&#xff0c;具体参数见数据手册吧&#xff08;CC1310数据手册、用户手册下载&#xff1a;https://dev.ti.com/tirex/explore/node?nodeA_…

pdf转换成图片转换器在线怎么转?pdf转换成图片具体方法介绍

很多用户们都是比较喜欢使用pdf文档的&#xff0c;由于这种文件格式的便携性非常高&#xff0c;所以广泛的应用于工作和学习领域&#xff0c;再加上pdf文档可以随意转换成为其他的文件格式&#xff0c;更是让pdf文档受到了更多用户们的欢迎&#xff0c;那么pdf转换成图片转换器…

如何在职业生涯中取得成功

工作中让你有强烈情绪波动的事情 在我的工作经历中&#xff0c;有一次让我经历了强烈情绪波动的事件。我曾在一个高压的项目团队中工作&#xff0c;我们需要在极短的时间内完成一个复杂的客户项目。这个项目的截止日期非常紧迫&#xff0c;而项目的规模和要求也一直在不断增加…

C++设计模式_02_面向对象设计原则

文章目录 1. 面向对象设计&#xff0c;为什么&#xff1f;2. 重新认识面向对象3. 面向对象设计原则3.1 依赖倒置原则(DIP)3.2 开放封闭原则(OCP )3.3 单一职责原则( SRP )3.4 Liskov 替换原则 ( LSP )3.5 接口隔离原则 ( ISP )3.6 优先使用对象组合&#xff0c;而不是类继承3.7…