rust学习

rust学习

    • String类型
    • clone和copy
    • 结构体的内存分布
    • for循环(<font color = red>important!)
    • 堆和栈
    • 数据结构
      • vector
    • panic
      • 失败就 panic: unwrap 和 expect
      • 传播错误
    • 模式匹配
      • 忽略模式的值
      • @绑定
    • 泛型
    • 特征Trait
      • 定义特征
      • 为类型实现特征
      • 孤儿规则
      • 使用特征作为函数参数
      • 特征约束(trait bound)
      • 函数返回中的impl Trait
      • 自定义类型打印输出
      • 关联类型
    • 方法和关联函数
    • 线程学习
      • 1.多线程的风险
      • 2.使用spawn创建线程
        • 等待子线程结束
        • move 关键字强制闭包获取其使用的环境值的所有权

Ok,Let’s rust !

String类型

String 类型

let mut s = String::from("hello world");

&String类型

let mut s1 = &s

字符串切片类型是&str

let s = String::from("hello");
let len = s.len();let slice = &s[0..len];
let slice = &s[..];

clone和copy

参考链接!link

结构体的内存分布

在这里插入图片描述

for循环(important!)

1..=5 代表 1 2 3 4 5
1..5  代表1 2 3 4

在这里插入图片描述
for in 结构能以几种方式与 Iterator 互动。在 迭代器 trait 一节将会谈到,如果没有特别指定,for 循环会对给出的集合应用 into_iter 函数,把它转换成一个迭代器。这并不是把集合变成迭代器的唯一方法,其他的方法有 iter 和iter_mut 函数。

下列三种情况刚好对应上面的表格!

  • iter
    在每次迭代中借用集合中的一个元素。这样集合本身不会被改变,循环之后仍可以使用。
fn main() {let names = vec!["Bob", "Frank", "Ferris"];for name in names.iter() {match name {&"Ferris" => println!("There is a rustacean among us!"),_ => println!("Hello {}", name),}}
}
  • info_iter
    会消耗集合。在每次迭代中,集合中的数据本身会被提供。一旦集合被消耗了,之后就无法再使用了,因为它已经在循环中被 “移除”(move)了。
fn main() {let names = vec!["Bob", "Frank", "Ferris"];for name in names.into_iter() {match name {"Ferris" => println!("There is a rustacean among us!"),_ => println!("Hello {}", name),}}
}
  • iter_mut
    可变地(mutably)借用集合中的每个元素,从而允许集合被就地修改。
fn main() {let mut names = vec!["Bob", "Frank", "Ferris"];for name in names.iter_mut() {*name = match name {&mut "Ferris" => "There is a rustacean among us!",_ => "Hello",}}println!("names: {:?}", names);
}

堆和栈

在很多语言中,你并不需要经常考虑到栈与堆。不过在像 Rust 这样的系统编程语言中,值是位于栈上还是堆上在更大程度上影响了语言的行为以及为何必须做出这样的抉择。我们会在本章的稍后部分描述所有权与栈和堆相关的内容,所以这里只是一个用来预热的简要解释。

栈和堆都是代码在运行时可供使用的内存,但是它们的结构不同。栈以放入值的顺序存储值并以相反顺序取出值。这也被称作 后进先出(last in, first out)。想象一下一叠盘子:当增加更多盘子时,把它们放在盘子堆的顶部,当需要盘子时,也从顶部拿走。不能从中间也不能从底部增加或拿走盘子!增加数据叫做 进栈(pushing onto the stack),而移出数据叫做 出栈(popping off the stack)。栈中的所有数据都必须占用已知且固定的大小。在编译时大小未知或大小可能变化的数据,要改为存储在堆上。 堆是缺乏组织的:当向堆放入数据时,你要请求一定大小的空间。内存分配器(memory allocator)在堆的某处找到一块足够大的空位,把它标记为已使用,并返回一个表示该位置地址的 指针(pointer)。这个过程称作 在堆上分配内存(allocating on the heap),有时简称为 “分配”(allocating)。(将数据推入栈中并不被认为是分配)。因为指向放入堆中数据的指针是已知的并且大小是固定的,你可以将该指针存储在栈上,不过当需要实际数据时,必须访问指针。想象一下去餐馆就座吃饭。当进入时,你说明有几个人,餐馆员工会找到一个够大的空桌子并领你们过去。如果有人来迟了,他们也可以通过询问来找到你们坐在哪。

入栈比在堆上分配内存要快,因为(入栈时)分配器无需为存储新数据去搜索内存空间;其位置总是在栈顶。相比之下,在堆上分配内存则需要更多的工作,这是因为分配器必须首先找到一块足够存放数据的内存空间,并接着做一些记录为下一次分配做准备。

访问堆上的数据比访问栈上的数据慢,因为必须通过指针来访问。现代处理器在内存中跳转越少就越快(缓存)。继续类比,假设有一个服务员在餐厅里处理多个桌子的点菜。在一个桌子报完所有菜后再移动到下一个桌子是最有效率的。从桌子 A 听一个菜,接着桌子 B 听一个菜,然后再桌子 A,然后再桌子 B 这样的流程会更加缓慢。出于同样原因,处理器在处理的数据彼此较近的时候(比如在栈上)比较远的时候(比如可能在堆上)能更好的工作。

当你的代码调用一个函数时,传递给函数的值(包括可能指向堆上数据的指针)和函数的局部变量被压入栈中。当函数结束时,这些值被移出栈。

跟踪哪部分代码正在使用堆上的哪些数据,最大限度的减少堆上的重复数据的数量,以及清理堆上不再使用的数据确保不会耗尽空间,这些问题正是所有权系统要处理的。一旦理解了所有权,你就不需要经常考虑栈和堆了,不过明白了所有权的主要目的就是为了管理堆数据,能够帮助解释为什么所有权要以这种方式工作。

fn main() {let mut x = 5;let mut y = x;x = 6;println!("{},{}", x, y)
}
输出6,5
为什么y不是6呢?因为普通i32变量在赋值的时候,只是值的拷贝,实际上他们的两个不同的5

数据结构

vector

使用宏自动推断类型
fn main() {let v = vec![1, 2, 3];
}更新
fn main() {let mut v = Vec::new();v.push(5);v.push(6);v.push(7);v.push(8);
}

panic

vscode默认打开的是powershell
查看更多运行错误信息的命令:$env:RUST_BACKTRACE=1;cargo run

失败就 panic: unwrap 和 expect

use std::fs::File;fn main() {let f = File::open("hello.txt").unwrap();
}

use std::fs::File;fn main() {let f = File::open("hello.txt").expect("Failed to open hello.txt");
}

expect 跟 unwrap 很像,也是遇到错误直接 panic, 但是会带上自定义的错误提示信息,相当于重载了错误打印的函数

传播错误

一般的错误

use std::fs::File;
use std::io::{self, Read};fn read_username_from_file() -> Result<String, io::Error> {// 打开文件,f是`Result<文件句柄,io::Error>`let f = File::open("hello.txt");let mut f = match f {// 打开文件成功,将file句柄赋值给fOk(file) => file,// 打开文件失败,将错误返回(向上传播)Err(e) => return Err(e),};// 创建动态字符串slet mut s = String::new();// 从f文件句柄读取数据并写入s中match f.read_to_string(&mut s) {// 读取成功,返回Ok封装的字符串Ok(_) => Ok(s),// 将错误向上传播Err(e) => Err(e),}
}

传播界的大明星 ?

use std::fs::File;
use std::io;
use std::io::Read;fn read_username_from_file() -> Result<String, io::Error> {let mut f = File::open("hello.txt")?;let mut s = String::new();f.read_to_string(&mut s)?;Ok(s)
}

**?**其实是一个宏,它的作用跟上面的match几乎一样

let mut f = match f {// 打开文件成功,将file句柄赋值给fOk(file) => file,// 打开文件失败,将错误返回(向上传播)Err(e) => return Err(e),
};

想象一下,一个设计良好的系统中,肯定有自定义的错误特征,错误之间很可能会存在上下级关系,例如标准库中的 std::io::Error 和 std::error::Error,前者是 IO 相关的错误结构体,后者是一个最最通用的标准错误特征,同时前者实现了后者,因此 std::io::Error 可以转换为 std:error::Error。

明白了以上的错误转换,? 的更胜一筹就很好理解了,它可以自动进行类型提升(转换):

fn open_file() -> Result<File, Box<dyn std::error::Error>> {let mut f = File::open("hello.txt")?;Ok(f)
}

上面代码中 File::open 报错时返回的错误是 std::io::Error 类型,但是 open_file 函数返回的错误类型是 std::error::Error 的特征对象,可以看到一个错误类型通过 ? 返回后,变成了另一个错误类型,这就是 ? 的神奇之处。

根本原因是在于标准库中定义的 From 特征,该特征有一个方法 from,用于把一个类型转成另外一个类型,? 可以自动调用该方法,然后进行隐式类型转换。因此只要函数返回的错误 ReturnError 实现了 From 特征,那么 ? 就会自动把 OtherError 转换为 ReturnError。

这种转换非常好用,意味着你可以用一个大而全的 ReturnError 来覆盖所有错误类型,只需要为各种子错误类型实现这种转换即可。

模式匹配

忽略模式的值

_s_ 有些微妙的不同:_s 会将值绑定到变量,而**_** 则完全不会绑定

fn main() {let s = Some(String::from("Hello!"));if let Some(_s) = s {println!("found a string");}println!("{:?}", s);
}
s 是一个拥有所有权的动态字符串,在上面代码中,我们会得到一个错误
因为 s 的值会被转移给 _s,在 println! 中再次使用 s 会报错但是如果使用_去匹配,则不会绑定值,因为s没有被移动到_

@绑定

@(读作 at)运算符允许为一个字段绑定另外一个变量。下面例子中,我们希望测试 Message::Hello 的 id 字段是否位于 3…=7 范围内,同时也希望能将其值绑定到 id_variable 变量中以便此分支中相关的代码可以使用它。我们可以将 id_variable 命名为 id,与字段同名,不过出于示例的目的这里选择了不同的名称。

enum Message {Hello { id: i32 },
}let msg = Message::Hello { id: 5 };match msg {Message::Hello { id: id_variable @ 3..=7 } => {println!("Found an id in range: {}", id_variable)},Message::Hello { id: 10..=12 } => {println!("Found an id in another range")},Message::Hello { id } => {println!("Found some other id: {}", id)},
}

@绑定到底有什么用呢,注意看第一个匹配和第二个匹配,当范围匹配的时候,无法直接得到这个值,这个时候,就可以使用@绑定,得到具体的值。

泛型

一个报错的泛型

fn largest<T>(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![34, 50, 25, 100, 65];let result = largest(&number_list);println!("The largest number is {}", result);let char_list = vec!['y', 'm', 'a', 'q'];let result = largest(&char_list);println!("The largest char is {}", result);
}error[E0369]: binary operation `>` cannot be applied to type `T` // `>`操作符不能用于类型`T`--> src/main.rs:5:17|
5 |         if item > largest {|            ---- ^ ------- T|            ||            T|
help: consider restricting type parameter `T` // 考虑对T进行类型上的限制 :|
1 | fn largest<T: std::cmp::PartialOrd>(list: &[T]) -> T {|             ++++++++++++++++++++++

因为 T 可以是任何类型,但不是所有的类型都能进行比较,因此上面的错误中,编译器建议我们给 T 添加一个类型限制:使用 std::cmp::PartialOrd 特征(Trait)对 T 进行限制,特征在下一节会详细介绍,现在你只要理解,该特征的目的就是让类型实现可比较的功能。

简单的add例子,不是所有 T 类型都能进行相加操作,因此我们需要用 std::ops::Add<Output = T> 对 T 进行限制:

fn add<T: std::ops::Add<Output = T>>(a:T, b:T) -> T {a + b
}

否则报错

error[E0369]: cannot add `T` to `T` // 无法将 `T` 类型跟 `T` 类型进行相加--> src/main.rs:2:7|
2 |     a + b|     - ^ - T|     ||     T|
help: consider restricting type parameter `T`|
1 | fn add<T: std::ops::Add<Output = T>>(a:T, b:T) -> T {|         +++++++++++++++++++++++++++

感觉这里的泛型跟java c++的还是不太一样,更抽象一点

特征Trait

特征类似于其他语言中的接口。在之前的代码中,我们也多次见过特征的使用,例如 #[derive(Debug)],它在我们定义的类型(struct)上自动派生 Debug 特征,接着可以使用 println!(“{:?}”, x) 打印这个类型;再例如:

fn add<T: std::ops::Add<Output = T>>(a:T, b:T) -> T {a + b
}

通过 std::ops::Add 特征来限制 T,只有 T 实现了 **std::ops::Add **才能进行合法的加法操作,毕竟不是所有的类型都能进行相加。
这些都说明一个道理,特征定义了一组可以被共享的行为,只要实现了特征,你就能使用这组行为。

定义特征

使用trait关键字来声明一个特征

pub trait Summary {fn summarize(&self) -> String;
}

这里使用 trait 关键字来声明一个特征,Summary 是特征名。在大括号中定义了该特征的所有方法,在这个例子中是: fn summarize(&self) -> String。
特征只定义行为看起来是什么样的,而不定义行为具体是怎么样的。因此,我们只定义特征方法的签名,而不进行实现,此时方法签名结尾是 ;而不是一个 {}

为类型实现特征

pub trait Summary {fn summarize(&self) -> String;
}
pub struct Post {pub title: String, // 标题pub author: String, // 作者pub content: String, // 内容
}impl Summary for Post {fn summarize(&self) -> String {format!("文章{}, 作者是{}", self.title, self.author)}
}

impl Summary for Post {}

孤儿规则

关于特征实现与定义的位置,有一条非常重要的原则:如果你想要为类型 A 实现特征 T,那么 A 或者 T 至少有一个是在当前作用域中定义的! 例如我们可以为上面的 Post 类型实现标准库中的 Display 特征,这是因为 Post 类型定义在当前的作用域中。同时,我们也可以在当前包中为 String 类型实现 Summary 特征,因为 Summary 定义在当前作用域中。

但是你无法在当前作用域中,为 String 类型实现 Display 特征,因为它们俩都定义在标准库中,其定义所在的位置都不在当前作用域,跟你半毛钱关系都没有,看看就行了。

该规则被称为孤儿规则,可以确保其它人编写的代码不会破坏你的代码,也确保了你不会莫名其妙就破坏了风马牛不相及的代码。

使用特征作为函数参数

pub fn notify(item: &impl Summary) {println!("Breaking news! {}", item.summarize());
}

impl Summary,只能说想出这个类型的人真的是起名鬼才,简直太贴切了,顾名思义,它的意思是 实现了Summary特征 的 item 参数。

特征约束(trait bound)

形如T: Summary 被称为特征约束

pub fn notify<T: Summary>(item: &T)pub fn notify(item1: &impl Summary, item2: &impl Summary) {}pub fn notify(item: &(impl Summary + Display)) {}pub fn notify<T: Summary + Display>(item: &T) {}fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 {}fn some_function<T, U>(t: &T, u: &U) -> i32where T: Display + Clone,U: Clone + Debug
{}

函数返回中的impl Trait

fn returns_summarizable() -> impl Summary {Weibo {username: String::from("sunface"),content: String::from("m1 max太厉害了,电脑再也不会卡",)}
}

因为 Weibo 实现了 Summary,因此这里可以用它来作为返回值。要注意的是,虽然我们知道这里是一个 Weibo 类型,但是对于 returns_summarizable 的调用者而言,他只知道返回了一个实现了 Summary 特征的对象,但是并不知道返回了一个 Weibo 类型。

自定义类型打印输出

#![allow(dead_code)] // 允许未使用的代码use std::fmt;
use std::fmt::{Display};#[derive(Debug,PartialEq)]
enum FileState {Open,Closed,
}#[derive(Debug)]
struct File {name: String,data: Vec<u8>,state: FileState,
}impl Display for FileState {// 第一个参数,当前实例的引用// 第二个参数引用类型,用于接收一个正在被修改的格式化输出流fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {match *self {FileState::Open => write!(f, "OPEN"),FileState::Closed => write!(f, "CLOSED"),}}
}impl Display for File {fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {write!(f, "<{} ({})>", self.name, self.state)}
}impl File {fn new(name: &str) -> File {File {name: String::from(name),data: Vec::new(),state: FileState::Closed,}}
}/*** &self:表示这个函数接受一个指向当前实例的引用。* * &mut fmt::Formatter:表示这个函数接受一个指向一个可以写入的 fmt::Formatter 的引用* fmt::Formatter 是用于格式化字符串的一种结构体。* * -> fmt::Result:表示这个函数返回一个 fmt::Result 值* fmt::Result 是 write!() 函数的返回类型,它表示是否成功地将数据写入到指定的 fmt::Formatter 上。* * write!(f, "<{} ({})>", self.name, self.state):* 这是调用 write!() 函数来将格式化的字符串写入到指定的 fmt::Formatter 中* 这个字符串包含了 self.name(即文件名)和 self.state(即文件状态* 其中,<...> 是一个字符串插槽,它会在运行时被替换为我们想要显示的内容* {} 后面的内容会被替换为对应的变量或表达式的值。在这个例子中,self.name 和 self.state 就是这些内容。*/fn main() {let f6 = File::new("f6.txt"); // 创建一个名为"f6.txt"的新文件对象println!("{:?}", f6); // 打印调试信息,输出文件对象的调试表示println!("{}", f6); // 打印文件对象的字符串表示
}

{:?} 使用的是 Debug 特性,会输出 File 对象的详细信息,包括其所有的字段及其值。
{} 使用的是 Display 特性,会输出 File 对象的字符串表示形式。在上面的例子中,由于我们为 File 类型实现了 Display 特性,所以 {} 会输出 <f6.txt (Open)> 这样的字符串

关联类型

type关键字可以用来取别名,但是在结合泛型使用的时候,是关联类型
关联类型是 trait 定义中的类型占位符。定义的时候,并不定义它的具体的类型是什么。在 impl 这个 trait 的时候,才为这个关联类型赋予确定的类型。也就是说,在实现的时候,才知道它的具体类型是什么。

pub trait Converter {type Output;fn convert(&self) -> Self::Output;
}struct MyInt;impl Converter for MyInt {type Output = i32;fn convert(&self) -> Self::Output {42}
}fn main() {let my_int = MyInt;let output = my_int.convert();println!("output is: {}", output);
}输出:output is: 42

参考

方法和关联函数

new 是关联函数,width是方法

pub struct Rectangle {width: u32,height: u32,
}impl Rectangle {pub fn new(width: u32, height: u32) -> Self {Rectangle { width, height }}pub fn width(&self) -> u32 {return self.width;}
}fn main() {let rect1 = Rectangle::new(30, 50);println!("{}", rect1.width());
}//  let rect1 = Rectangle::new(30, 50);

线程学习

1.多线程的风险

由于多线程的代码是同时运行的,因此我们无法保证线程间的执行顺序,这会导致一些问题:

  1. 竞态条件(race conditions),多个线程以非一致性的顺序同时访问数据资源
  2. 死锁(deadlocks),两个线程都想使用某个资源,但是又都在等待对方释放资源后才能使用,结果最终都无法继续执行
  3. 一些因为多线程导致的很隐晦的 BUG,难以复现和解决

虽然 Rust 已经通过各种机制减少了上述情况的发生,但是依然无法完全避免上述情况,因此我们在编程时需要格外的小心,同时本书也会列出多线程编程时常见的陷阱,让你提前规避可能的风险。

2.使用spawn创建线程

use std::thread;
use std::time::Duration;fn main() {thread::spawn(|| {for i in 1..10 {println!("hi number {} from the spawned thread!", i);thread::sleep(Duration::from_millis(1));}});for i in 1..5 {println!("hi number {} from the main thread!", i);thread::sleep(Duration::from_millis(1));}
}

但是打印顺序是随机的,主线程和子线程会随机打印。有可能主线程执行完了,子线程还没有创建,这取决于操作系统的调度。

等待子线程结束
use std::thread;
use std::time::Duration;fn main() {let handle = thread::spawn(|| {for i in 1..5 {println!("hi number {} from the spawned thread!", i);thread::sleep(Duration::from_millis(1));}});handle.join().unwrap();for i in 1..5 {println!("hi number {} from the main thread!", i);thread::sleep(Duration::from_millis(1));}
}
通过调用 handle.join,可以让当前线程阻塞,直到它等待的子线程的结束
在上面代码中,由于 main 线程会被阻塞,因此它直到子线程结束后才会输出自己的 1..5
move 关键字强制闭包获取其使用的环境值的所有权
use std::thread;fn main() {let v = vec![1, 2, 3];let handle = thread::spawn(|| {println!("Here's a vector: {:?}", v);});handle.join().unwrap();
}

子线程要v的所有权,但是子线程和主线程执行顺序随机。在主线程丢弃,使用drop也不行。根本原因在于存在所有权冲突。

正确写法——添加move关键字

直接将所有权转移到子线程

use std::thread;fn main() {let v = vec![1, 2, 3];let handle = thread::spawn(move || {println!("Here's a vector: {:?}", v);});handle.join().unwrap();// 下面代码会报错borrow of moved value: `v`// println!("{:?}",v);
}

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

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

相关文章

Centos安装RabbitMQ,JavaSpring发送RabbitMQ延迟延时消息,JavaSpring消费RabbitMQ消息

1&#xff0c;版本说明 erlang 和 rabbitmq 版本说明 https://www.rabbitmq.com/which-erlang.html 确认需要安装的mq版本以及对应的erlang版本。 2&#xff0c;下载安装文件 RabbitMQ下载地址&#xff1a; https://packagecloud.io/rabbitmq/rabbitmq-server Erlang下载地…

【C++项目】高并发内存池第七讲性能分析

目录 1.测试代码2.代码介绍3.运行结结果 1.测试代码 #include"ConcurrentAlloc.h" #include"ObjectPool.h" #include"Common.h" void BenchmarkMalloc(size_t ntimes, size_t nworks, size_t rounds) {std::vector<std::thread> vthread(…

接口自动化测试要做什么?一文3个步骤带你成功学会!

先了解下接口测试流程&#xff1a; 1、需求分析 2、Api文档分析与评审 3、测试计划编写 4、用例设计与评审 5、环境搭建&#xff08;工具&#xff09; 6、执行用例 7、缺陷管理 8、测试报告 了解了接口测试的工作流程&#xff0c;那"接口自动化测试"怎么弄&#xff1…

可视化 | (三)Edward Tufted基本设计准则

文章目录 &#x1f4da;Edward Tufted基本设计准则&#x1f407;Graphical Integrity&#x1f407;Lie Factor&#x1f407;Data-Ink&#x1f407;Chart Junks &#x1f4da;其他注意事项&#x1f407;Pie Charts&#x1f407;Rainbow Colormap&#x1f407;3D charts&#x1f…

基于华为云 IoT 物联网平台实现家居环境实时监控

01 智能家居环境监测 智能家居环境监测采用 Ruff 开发板作为主控&#xff0c;串口线连接温湿度传感器 DHT11 和空气质量传感器 SDS011&#xff0c;每5分钟采集一次数据&#xff0c;通过 MQTT 协议发送到华为云 IoT 物联网平台&#xff0c;并基于数据分析服务实时计算出整个家庭…

【java学习—十】异常(1)

文章目录 1. 概念1.1. 前言1.2. java中的异常 2. java运行时异常举例3. 总结 1. 概念 1.1. 前言 任何一种程序设计语言设计的程序在运行时都有可能出现错误&#xff0c;例如除数为 0 &#xff0c;数组下标越界&#xff0c;要读写的文件不存在等等。     捕获错误最理想的是…

【网络】详解http协议

目录 一、认识URLurlencode和urldecode 二、HTTP协议HTTP协议格式HTTP的方法HTTP的状态码HTTP常见Header 一、认识URL URL叫做统一资源定位符&#xff0c;也就是我们平时俗称的网址&#xff0c;是因特网的万维网服务程序上用于指定信息位置的表示方法。 urlencode和urldecode …

asp.net学生考试报名管理系统VS开发sqlserver数据库web结构c#编程Microsoft Visual Studio

一、源码特点 asp.net学生考试报名管理系统是一套完善的web设计管理系统系统&#xff0c;系统具有完整的源代码和数据库&#xff0c;系统主要采用B/S模式开发。开发环境为vs2010&#xff0c;数据库为sqlserver2008&#xff0c;使 用c#语言开发 应用技术&#xff1a;asp…

MySQL数据库#6

Python操作mysql 在使用Python连接mysql之前我们需要先下载一个第三方的模块 pymysql的模块&#xff0c;导入后再进行操作。 操作步骤&#xff1a;1. 先连接mysql host&#xff0c;port&#xff0c;charset&#xff0c;username password 库&#xff0c;等等。 import pymysql…

面试题之JavaScript经典for循环(var let)

如果你也在面试找工作&#xff0c;那么也一定遇到过这道for循环打印结果的题&#xff0c;下面我们来探讨下 var循环 for(var i 0; i < 10; i) {setTimeout(function(){console.log(i)}); } 先把答案写出来 下面来讲一下原因&#xff1a; 划重点 ① var ②setTimeout() …

10款轻量型的嵌入式GUI库分享

LVGL LittlevGL是一个免费的开源图形库&#xff0c;提供了创建嵌入式GUI所需的一切&#xff0c;具有易于使用的图形元素、漂亮的视觉效果和低内存占用。 特点&#xff1a; 强大的构建模组 按钮、图表、列表、滑块、图像等 ​先进的图形 动画、反锯齿、半透明、平滑滚动 多样…

Elasticsearch核心技术与实战-05-elasticsearch的安装与简单配置-Windows

首先下载elasticsearch的zip包&#xff1a;下载地址 网络不通的解决方法&#xff1a;国内镜像站 es、kibana、logstash均可在华为云开元镜像站自行选择版本下载&#xff1a;下载地址 下载插件包&#xff1a; .\bin\elasticsearch-plugin install analysis-icu .\bin\elasti…

LLM在text2sql上的应用 | 京东云技术团队

一、前言&#xff1a; 目前&#xff0c;大模型的一个热门应用方向text2sql它可以帮助用户快速生成想要查询的SQL语句。那对于用户来说&#xff0c;大部分简单的sql都是正确的&#xff0c;但对于一些复杂逻辑来说&#xff0c;需要用户在产出SQL的基础上进行简单修改&#xff0c…

【Apache Flink】基于时间和窗口的算子-配置时间特性

文章目录 前言配置时间特性将时间特性设置为事件时间时间戳分配器周期性水位线分配器创建一个实现AssignerWithPeriodicWatermarks接口的类&#xff0c;目的是为了周期性生成watermark 定点水位线分配器示例 参考文档 前言 Apache Flink 它提供了多种类型的时间和窗口概念&…

C#WinformListView实现缺陷图片浏览器

C#&Winform&ListView实现缺陷图片浏览器 功能需求图像浏览行间距调整悬浮提示 功能需求 机器视觉检测系统中特别是缺陷检测系统&#xff0c;通常需要进行对已经检出的缺陷图片进行浏览查阅。主要是通过条件筛选查询出所需要的数据&#xff0c;进行分页再展示到界面中。…

[计算机提升] Windows系统各种开机启动方式介绍

1.14 开机启动 在Windows系统中&#xff0c;开机启动是指开启电脑后&#xff0c;自动运行指定的程序或服务的技术。一些程序或服务需要在开机后自动启动&#xff0c;以便及时响应用户操作&#xff0c;比如防安防软件、即时通信工具、文件同步软件等。 同时&#xff0c;一些系统…

深入剖析SQL与NoSQL的优劣势,帮你决定最佳数据存储方案

你是否在为系统的数据库来一波大流量就几乎打满 CPU&#xff0c;日常 CPU 居高不下烦恼?你是否在各种 NoSQL 间纠结不定&#xff0c;到底该选用哪种最好?今天的你就是昨天的我&#xff0c;这也是我写这篇文章的初衷。 作为互联网从业人员&#xff0c;我们要知道关系型数据库…

电感基础复盘

1、在高速电路中&#xff0c;我们通常选用SMD贴片电阻&#xff0c;有薄膜和厚膜之分。 2、电容的性质主要为“充放电”和”隔直通交“。获得电荷为充电&#xff0c;反之为放电。隔离直流电不能通过电容器&#xff0c;⽽交流电能通过电容器。充电时直流电相当于导通&#xff0c;…

FPGA时序分析与约束(7)——通过Tcl扩展SDC

一、概述 术语“Synopsys公司设计约束”&#xff08;又名SDC&#xff0c;Synopsys Design Constraints&#xff09;用于描述对时序、功率和面积的设计要求&#xff0c;是EDA工具中用于综合、STA和布局布线最常用的格式。本文介绍时序约束的历史概要和SDC的描述。 二、时序约束…

python:使用Scikit-image对遥感影像进行小波变换特征提取(wavelet)

作者:CSDN @ _养乐多_ 在本博客中,我们将介绍如何使用Scikit-image库进行单波段遥感图像的特征提取,重点关注小波变换方法,特别是Gabor滤波器。我们将详细解释代码中的参数以及如何调整它们以满足不同需求。 小波变换是一种数学工具,用于将信号分解成不同尺度和频率的成…