当前位置: 首页 > news >正文

【Rust自学】10.4. trait Pt.2:trait作为参数和返回类型、trait bound

喜欢的话别忘了点赞、收藏加关注哦,对接下来的教程有兴趣的可以关注专栏。谢谢喵!(=・ω・=)

说句题外话,写这篇的时间比写所有权还还花的久,trait是真的比较难理解的概念。
请添加图片描述

10.4.1. 把trait作为参数

继续以上一篇文章中所讲的内容作为例子:

pub trait Summary {fn summarize(&self) -> String;
}pub struct NewsArticle {pub headline: String,pub location: String,pub author: String,pub content: String,
}impl Summary for NewsArticle {fn summarize(&self) -> String {format!("{}, by {} ({})", self.headline, self.author, self.location)}
}pub struct Tweet {pub username: String,pub content: String,pub reply: bool,pub retweet: bool,
}impl Summary for Tweet {fn summarize(&self) -> String {format!("{}: {}", self.username, self.content)}
}

在这里我们再新定义一个函数notify,这函数接收NewsArticleTweet两个结构体,打印:“Breaking news:”,后面的内容是在参数上调用Summary上的summerize方法的返回值。

但这里有一个问题,它接收的参数是两个结构体,怎么样实现让参数可以是两个类型呢?

我们细想一下,这两个结构体的共同点是什么?没错,它们都实现了Summary这个trait。Rust对于这种情况提供了解决方案:

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

只要把参数类型写成impl 某个trait就可以,这里两个结构体都实现了Summary这个trait,所以就写impl Summary,而又因为这个函数不需要数据的所有权,所以写成引用&impl Summary即可。如果又有其它数据类型实现了Summary,那它照样可以作为参数传进去。

impl trait的语法适用于简单情况,针对复杂情况,一般使用trait bound语法。

同样是上面的代码,用trait bound这么写:

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

这两种写法等价。

但是这种简单的写法看不出来trait bound的优势,再换一个例子。比如说,我要设计一个新的nnotify函数,叫它notify1吧,它接收两个参数,输出"Breaking news:"后面的内容是两个参数分别调用Summary上的summerize方法的返回值。

trait bound写法:

pub fn notify1<T: Summary>(item1: &T, item2: &T) {  println!("Breaking news! {} {}", item1.summarize(), item2.summarize());  
}

impl trait写法:

pub fn notify1(item1: &impl Summary, item2: &impl Summary) {  println!("Breaking news! {} {}", item1.summarize(), item2.summarize());  
}

前一种的函数签名显然比后一种的要跟好写也更直观。

而实际上,impl trait写法不过是trait bound写法的语法糖,所以impl trait写法不适合复杂情况也确实可以理解。

那么如果这个notify函数我需要它的参数是同时实现Display这个trait和Summary这个trait呢?也就是如果我有两个甚至两个以上的trait bounds该怎么写呢?

看例子:

pub fn notify_with_display<T: Summary + std::fmt::Display>(item: &T) {  println!("Breaking news! {}", item);  
}

使用+号连接各个trait bound即可

还有一点,由于Display不在预导入模块,所以写它的时候需要把路径写出来,也可以在代码开头先引入Display这个trait,也就是写use std::fmt::Display,这样就可以在写trait bound时直接写Display

use std::fmt::Displaypub fn notify_with_display<T: Summary + Display>(item: &T) {  println!("Breaking news! {}", item);  
}

别忘了impl trait这个语法糖哦,在这个语法糖里也是用+连接trait bounds:

use std::fmt::Displaypub fn notify_with_display(item: &impl Summary + Display) {  println!("Breaking news! {}", item);  
}

这种写法有一个缺点,如果trait bounds过多,那么写的大量约束信息就会降低这个函数签名的可读性。为了解决这个问题,Rust提供了替代语法,就是在函数签名之后使用where字句来写trait bounds

看个使用普通写法的写多个trait bounds:

use std::fmt::Display;  
use std::fmt::Debug;pub fn special_notify<T: Summary + Display, U: Summary + Debug>(item1: &T, item2: &U) {  format!("Breaking news! {} and {}", item1.summarize(), item2.summarize());  
}

使用where字句重写的代码:

use std::fmt::Display;  
use std::fmt::Debug;pub fn special_notify<T, U>(item1: &T, item2: &U)   
where  T: Summary + Display,  U: Summary + Debug,  
{  format!("Breaking news! {} and {}", item1.summarize(), item2.summarize());  
}

这种写法跟C#很相似。

10.4.2. 把trait作为返回类型

跟作为参数一样,把trait作为返回值也可以使用impl trait。如下例:

fn returns_summarizable() -> impl Summary {  Tweet {  username: String::from("horse_ebooks"),  content: String::from(  "of course, as you probably already know, people",  ),  reply: false,  retweet: false,  }  
}

这个语法有一个缺点:如果让返回类型实现了某个trait,那么必须保证这个函数/方法它所有的可能返回值都只能是一个类型。这是因为impl写法在工作上有一些限制导致Rust不支持。但Rust支持动态派发,之后会讲。

举个例子:

fn returns_summarizable(flag:bool) -> impl Summary {  if flag {  Tweet {  username: String::from("horse_ebooks"),  content: String::from(  "of course, as you probably already know, people",  ),  reply: false,  retweet: false,  }  } else {  NewsArticle {  headline: String::from("Penguins win the Stanley Cup Championship!"),  location: String::from("Pittsburgh, PA, USA"),  author: String::from("Iceburgh, Scotland"),  content: String::from(  "The Pittsburgh Penguins once again are the best \  hockey team in the NHL.",  ),  }  }  
}

根据flag的布尔值一共有两种可能的返回值类型:Tweet类型和NewArticle,这时候编译器就会报错:

error[E0308]: `if` and `else` have incompatible types--> src/lib.rs:42:9|
32 | /       if flag {
33 | | /         Tweet {
34 | | |         username: String::from("horse_ebooks"),
35 | | |         content: String::from(
36 | | |             "of course, as you probably already know, people",
...  | |
39 | | |         retweet: false,
40 | | |         }| | |_________- expected because of this
41 | |       } else {
42 | | /         NewsArticle {
43 | | |             headline: String::from("Penguins win the Stanley Cup Championship!"),
44 | | |             location: String::from("Pittsburgh, PA, USA"),
45 | | |             author: String::from("Iceburgh, Scotland"),
...  | |
49 | | |             ),
50 | | |         }| | |_________^ expected `Tweet`, found `NewsArticle`
51 | |       }| |_______- `if` and `else` have incompatible types|
help: you could change the return type to be a boxed trait object|
31 | fn returns_summarizable(flag:bool) -> Box<dyn Summary> {|                                       ~~~~~~~        +
help: if you change the return type to expect trait objects, box the returned expressions|
33 ~         Box::new(Tweet {
34 |         username: String::from("horse_ebooks"),
...
39 |         retweet: false,
40 ~         })
41 |     } else {
42 ~         Box::new(NewsArticle {
43 |             headline: String::from("Penguins win the Stanley Cup Championship!"),
...
49 |             ),
50 ~         })|

报错内容就是ifelse下的返回类型是不兼容的(也就是不是同一种类型)。

使用trait bounds的实例

还记得在 10.2. 泛型 中提到的比大小的代码吗?我把代码粘在这里:

fn largest<T>(list: &[T]) -> T{  let mut largest = list[0];  for &item in list{  if item > largest{  largest = item;  }  }  largest  
}

当时这么写报的错我也粘在这里:

error[E0369]: binary operation `>` cannot be applied to type `T`--> src/main.rs:4:17|
4 |         if item > largest{|            ---- ^ ------- T|            ||            T|
help: consider restricting type parameter `T`|
1 | fn largest<T: std::cmp::PartialOrd>(list: &[T]) -> T{|             ++++++++++++++++++++++

在学了trait之后,是不是对这种写法和这个报错信息的理解又不同了呢?

先从报错代码来分析,报错信息是比较大小的运算符>不能应用在类型T上,下面的help这行又写了考虑限制类型参数T,再往下看下面还写到了具体的做法,就是在T后面添加std::cmp::PartialOrd(在trait bound里只需要写PartialOrd,因为它在预导入模块内,所以不需要把路径写全),这实际上是一个用于实现比较大小的trait,试试按照提示来改:

fn largest<T: PartialOrd>(list: &[T]) -> T{  let mut largest = list[0];  for &item in list{  if item > largest{  largest = item;  }  }  largest  
}

还是报错:

error[E0508]: cannot move out of type `[T]`, a non-copy slice--> src/main.rs:2:23|
2 |     let mut largest = list[0];|                       ^^^^^^^|                       ||                       cannot move out of here|                       move occurs because `list[_]` has type `T`, which does not implement the `Copy` trait|
help: if `T` implemented `Clone`, you could clone the value--> src/main.rs:1:12|
1 | fn largest<T: std::cmp::PartialOrd>(list: &[T]) -> T{|            ^ consider constraining this type parameter with `Clone`
2 |     let mut largest = list[0];|                       ------- you could clone this value
help: consider borrowing here|
2 |     let mut largest = &list[0];|                       +

但报的错不一样了:无法从list里移动元素,因为list里的T没有实现Copy这个trait,下边的help说如果T实现了Clone这个trait,考虑克隆这个值。再下面还有一个help,说考虑使用借用的形式。

根据以上信息,有三种解决方案:

  • 为泛型添加上Copy这个trait
  • 使用克隆(得为泛型加上Clone这个trait)
  • 使用借用

该选择哪个解决方案呢?这取决于你的需求。我想要这个函数能够处理数字和字符的集合,由于数字和字符都是存储在栈内存上的,所以都实现了Copy这个trait,那么只需要为泛型添加上Copy这个trait就可以:

fn largest<T: PartialOrd + Copy>(list: &[T]) -> T{    let mut largest = list[0];    for &item in list{    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);  
}

输出:

The largest number is 100
The largest char is y

那如果我想要这个函数实现String集合的对比呢?由于String是存储在堆内存上的,所以它并没有实现Copy这个trait,所以为泛型添加上Copy这个trait的思路就行不通。

那就试试克隆(得为泛型加上Clone这个trait):

fn largest<T: PartialOrd + Clone>(list: &[T]) -> T{    let mut largest = list[0].clone();    for &item in list.iter() {    if item > largest{    largest = item;    }    }    largest    
}  fn main() {  let string_list = vec![String::from("dev1ce"), String::from("Zywoo")];  let result = largest(&string_list);  println!("The largest string is {}", result);  
}

输出:

error[E0507]: cannot move out of a shared reference--> src/main.rs:3:18|
3 |     for &item in list.iter() {  |          ----    ^^^^^^^^^^^|          ||          data moved here|          move occurs because `item` has type `T`, which does not implement the `Copy` trait|
help: consider removing the borrow|
3 -     for &item in list.iter() {  
3 +     for item in list.iter() {  |

错误是数据无法移动,因为这种写法要求实现Copy这个trait,但String做不到,该怎么办呢?

那就不让数据移动,不要使用模式匹配,去掉&item前的&,这样item就从T变为了不可变引用&T。然后在比较的时候再使用解引用符号*,把&T解引用为T来与largest比较(下面的代码使用的就是这种),或在largest前加&来变为&T,总之要保持比较的两个变量类型一致:

fn largest<T: PartialOrd + Clone>(list: &[T]) -> T{    let mut largest = list[0].clone();    for item in list.iter() {    if *item > largest{    largest = item.clone();    }    }    largest    
}fn main() {  let string_list = vec![String::from("dev1ce"), String::from("Zywoo")];  let result = largest(&string_list);  println!("The largest string is {}", result);  
}

记住T没有实现Copy这个trait,所以在给largest时要使用clone方法。

输出:

The largest string is dev1ce

这里这么写是因为返回值是T,如果把返回值改为&T就不需要克隆了:

fn largest<T: PartialOrd>(list: &[T]) -> &T{      let mut largest = &list[0];      for item in list.iter() {      if item > &largest{      largest = item;      }      }      largest      
}  fn main() {  let string_list = vec![String::from("dev1ce"), String::from("Zywoo")];  let result = largest(&string_list);  println!("The largest string is {}", result);  
}

但是记住,得在largest初始化时得把它设为&T,所以list[0]前得加上&表示引用。而且比较的时候也不能使用给item解引用的方法而得给largest&

10.4.3. 使用trait bound有条件的实现方法

在使用泛型类型参数的impl块上使用trait boud,就可以有条件地为实现了特定trait的类型来实现方法。

看个例子:

use std::fmt::Display;  struct Pair<T> {  x: T,  y: T,  
}  impl<T> Pair<T> {  fn new(x: T, y: T) -> Self {  Self { x, y }  }  
}  impl<T: Display + PartialOrd> Pair<T> {  fn cmp_display(&self) {  if self.x >= self.y {  println!("The largest member is x = {}", self.x);  } else {  println!("The largest member is y = {}", self.y);  }  }  
}

无论T具体是什么类型,在Pair上都会有new函数,但只有T实现了DisplayPartialOrd的时候才会有cmd_display这个方法。

也可以为实现了其它trait的任意类型有条件的实现某个trait。为满足trait bound的所有类型上实现trait叫做覆盖实现(blanket implementations)

以标准库中的to_string函数为例:

impl<T: Display> ToString for T {// ......
}

它的意思就是对所满足display trait的类型都实现了ToString这个trait,这就是所谓的覆盖实现,也就是可以为任何实现了display trait的类型调用ToString这个trait上的方法。

以整数为例:

let s = 3.to_string();

这个操作之所以能实现是因为i32实现了Display trait,所以可以调用ToString上的to_string方法。

相关文章:

【Rust自学】10.4. trait Pt.2:trait作为参数和返回类型、trait bound

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 说句题外话&#xff0c;写这篇的时间比写所有权还还花的久&#xff0c;trait是真的比较难理解的概念。 10.4.1. 把trait作为参数 继续以…...

Java 中的 getDeclaredMethod() 方法:使用与原理详解

在 Java 反射机制中&#xff0c;getDeclaredMethod() 是一个非常重要的方法&#xff0c;用于获取类中声明的特定方法&#xff08;包括公共、保护、默认和私有方法&#xff09;。与 getMethod() 不同&#xff0c;getDeclaredMethod() 可以访问类的所有方法&#xff0c;而不仅仅是…...

解决npm报错:sill idealTree buildDeps

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 报错信息 使用 npm 安装依赖时报错&#xff1a;sill idealTree buildDeps 解决方案 请按照以下步骤进行相关操作&#xff1a; 1、删除 C:\Users{账户}\ 文件夹中的 .npm…...

【游戏设计原理】55 - 风险评估

从本文讲述的内容来看&#xff0c;其实使用“游戏中的决策”作为标题更合适。 核心观点&#xff1a;玩家在游戏中不断进行决策&#xff0c;这些决策涵盖风险评估、资源分配、策略选择等多个方面&#xff0c;其核心是通过选择实现最大化回报或最小化损失。关键内容&#xff1a;…...

【AI日记】25.01.08

【AI论文解读】【AI知识点】【AI小项目】【AI战略思考】【AI日记】【读书与思考】 AI 参加&#xff1a;kaggle 比赛 Forecasting Sticker Sales 读书 书名&#xff1a;国家为什么会失败阅读原因&#xff1a;2024 年诺贝尔经济学奖得主的力作未删减版&#xff1a;https://boo…...

SAP BC 同服务器不同client之间的传输SCC1

源配置client不需要释放 登录目标client SCC1...

用Cline打造你的智能搜索助手:Tavily Search MCP集成指南

引言 本文将详细介绍如何在Cline编辑器中集成Tavily Search智能搜索功能。我们将从MCP&#xff08;Model Context Protocol&#xff09;协议基础开始&#xff0c;深入探讨Tavily Search MCP服务器的安装配置、使用方法&#xff0c;以及进阶的二次开发技巧。无论你是AI开发者还…...

未来商贸物流:人工智能与大数据的深度融合

未来商贸物流&#xff1a;人工智能与大数据的深度融合 在当今数字化浪潮汹涌澎湃的时代&#xff0c;商贸物流行业正站在变革的十字路口&#xff0c;而人工智能与大数据宛如一对闪耀的双子星&#xff0c;为其照亮前行的道路&#xff0c;深度融合之下&#xff0c;一个全新的未来…...

网络安全、Web安全、渗透测试之笔经面经总结(三)

本篇文章涉及的知识点有如下几方面&#xff1a; 1.什么是WebShell? 2.什么是网络钓鱼&#xff1f; 3.你获取网络安全知识途径有哪些&#xff1f; 4.什么是CC攻击&#xff1f; 5.Web服务器被入侵后&#xff0c;怎样进行排查&#xff1f; 6.dll文件是什么意思&#xff0c;有什么…...

BMS应用软件开发 — 3 电池系统的组成

目录 1 电池的基本拓扑 2 已经被淘汰的CTM 3 早已经普及的CTP 4 集成度更高的CTC 5 刚性更好的CTB 1 电池的基本拓扑 相比于燃油车&#xff0c;虽然电动车在结构空间上灵活度更高&#xff0c;空间利用率也更好&#xff0c;但现有条件下无法像燃油车一样快速补能&#xff…...

springboot 项目使用nacos注册中心配置,在windows系统下打jar包后不能启动的一个不好排查的问题

起因&#xff0c;一个项目使用的nacos注册中心配置&#xff0c;想学习运维一些知识&#xff0c;项目在本地idea&#xff0c;无论是run&#xff0c;debug模式&#xff0c;都可以正常运行。就学习如何打包&#xff0c;打包好后&#xff0c;如何运jar包&#xff0c;都启动不起来&a…...

Git撤销指定commit并更新远端仓库

Git撤销指定commit并更新远端仓库 一、撤销指定commit 1.首先执行git log 命令&#xff0c;查看git历史提交以及commit信息&#xff1a; 由于需要脱敏&#xff0c;所以截图可能看得马赛克比较多&#xff0c;需要关注的就是上面的commit后跟的id&#xff0c;以及HEAD当前指定…...

校园约拍微信小程序设计与实现ssm+论文源码调试讲解

4 系统设计 校园约拍微信小程序的设计方案比如功能框架的设计&#xff0c;比如数据库的设计的好坏也就决定了该系统在开发层面是否高效&#xff0c;以及在系统维护层面是否容易维护和升级&#xff0c;因为在系统实现阶段是需要考虑用户的所有需求&#xff0c;要是在设计阶段没…...

开源靶场1

我来为您介绍一些知名的开源漏洞靶场平台: DVWA (Damn Vulnerable Web Application) 最流行的 Web 漏洞靶场之一包含 SQL 注入、XSS、文件包含等常见漏洞基于 PHP MySQL适合 Web 安全入门学习 WebGoat OWASP 开源项目基于 Java包含大量 Web 安全漏洞练习提供详细的教程和解…...

iOS开发指南:保护服务器密码的安全存储与处理技巧

在iOS开发过程中&#xff0c;服务器密码的安全存储与处理是确保应用安全性的关键环节。不当的密码管理可能导致数据泄露、用户隐私受损&#xff0c;甚至引发更严重的安全问题。因此&#xff0c;开发者需要采取一系列措施来保护服务器密码的安全。本文将详细介绍在iOS开发中如何…...

《Spring Framework实战》9:4.1.4.依赖注入

欢迎观看《Spring Framework实战》视频教程 典型的企业应用程序不是由单个对象&#xff08;或Spring术语中的bean&#xff09;组成。即使是最简单的应用程序也有几个对象协同工作&#xff0c;以呈现最终用户所认为的连贯应用程序。下一节将解释如何从定义多个独立的bean定义到一…...

linux之自动挂载

如果想要实现自动挂载&#xff0c;应该挂在客户端&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; 客户端&#xff1a; [rootlocalhost ~]# yum install nfs-utils -y &#xff08;下载软件&#xff09; [rootlocalhost ~]# systemctl start nfs-utils.servic…...

python+fpdf:创建pdf并实现表格数据写入

目录 创建pdf文件对象 新增页 添加自定义字体 设置字体 设置文字颜色和背景色 插入内容 换行 插入图片 保存pdf 完整代码 安装:pip install fpdf 创建pdf文件对象 from fpdf import FPDF, Alignpdf = FPDF() # 创建pdf文件对象 获取边距 print(pdf.l_margin, p…...

比较procfs 、 sysctl和Netlink

procfs 文件系统和 sysctl 的使用: procfs 文件系统(/proc) procfs 文件系统是 Linux 内核向用户空间暴露内核数据结构以及配置信息的一种方式。`procfs` 的挂载点是 /proc 目录,这个目录中的文件和目录呈现内核的运行状况和配置信息。通过读写这些文件,可以查看和控制内…...

第四、五章补充:线代本质合集(B站:小崔说数)

视频1&#xff1a;线性空间 原视频&#xff1a;【线性代数的本质】向量空间、基向量的几何解释_哔哩哔哩_bilibili 很多同学在学习线性代数的时候&#xff0c;会遇到一个困扰&#xff0c;就是不知道什么是线性空间。因为中文的教材往往对线性空间的定义是非常偏数学的&#x…...

Clojure语言的函数实现

Clojure语言函数实现详解 引言 Clojure是一种基于Java虚拟机的函数式编程语言&#xff0c;以其简洁和强大的数据处理能力而著称。在Clojure中&#xff0c;函数是基本的构造块&#xff0c;良好的函数设计可以提高代码的可复用性和可维护性。本文将深入探讨Clojure中的函数实现…...

Windows10环境下安装RabbitMq折腾记

最近有个老项目需要迁移到windows10环境&#xff0c;用的是比较老的rabbitmq安装包&#xff0c;如下所示。经过一番折腾&#xff0c;死活服务起不来&#xff0c;最终果断放弃老版本启用新版本。现在把折腾过程记录下&#xff1a; 一、安装erlang 安装完成后的目录结构&#xff…...

【前端,TypeScript】TypeScript速成(九):async-await 语法糖

async-await 语法糖 可以使用 async-await 来管理 Promise&#xff0c;下例重写上一节使用 Promise then 的形式计算 ( 2 3 ) ( 4 5 ) (23) \times (45) (23)(45)&#xff1a; function add(a: number, b: number): Promise<number>{return new Promise((resolve,…...

基于ADAS 与关键点特征金字塔网络融合的3D LiDAR目标检测原理与算法实现

一、概述 3D LiDAR目标检测是一种在三维空间中识别和定位感兴趣目标的技术。在自动驾驶系统和先进的空间分析中&#xff0c;目标检测方法的不断演进至关重要。3D LiDAR目标检测作为一种变革性的技术&#xff0c;在环境感知方面提供了前所未有的准确性和深度信息. 在这里&…...

STM32供电参考设计

STM32供电参考设计 ​ 在图中有VDD&#xff0c;VSS和VDDA&#xff0c;VSSA两种类型的供电引脚&#xff0c;其数据手册解释如下&#xff1a; ​ 令我不解的是&#xff1a;VDDA和VSSA必须分别连接到VDD和VSS&#xff0c;这是什么意思&#xff1f;有大佬能够解答一下吗&#xff1f…...

C/C++头文件uitility

在C中&#xff0c;<utility>头文件提供了一些通用的工具类和函数&#xff0c;这些工具类和函数在编写高效、可读性强的代码时非常有用。以下是<utility>头文件中一些常用函数和类的详细介绍及使用示例&#xff1a; std::pair&#xff1a;一个模板类&#xff0c;用于…...

Nature Electronics——近传感器计算:50 nm异构集成技术的革命

创新点&#xff1a;1.高密度互联设计&#xff1a;基于二维材料&#xff0c;开发出互连密度高达62,500 I/O每平方毫米的M3D集成结构。2.异构层堆叠&#xff1a;整合了第二层石墨烯化学传感器和第一层MoS₂记忆晶体管&#xff0c;实现功能互补。3.超短传感器与计算元件距离&#…...

腾讯云AI代码助手编程挑战赛-图片转换工具

作品简介&#xff1a; 解决了人们学习生活中的图片格式转换问题&#xff0c; 制作该脚本&#xff0c;省去了打开在线编辑器操作的时间&#xff0c; 免费为用户提供图片格式的转换的实用小工具 技术架构 python语言的tk库来完成的GUI页面设计&#xff0c; 引用PIL包转换图…...

英伟达Project Digits赋能医疗大模型:创新应用与未来展望

英伟达Project Digits赋能医疗大模型&#xff1a;创新应用与未来展望 一、引言 1.1 研究背景与意义 在当今数字化时代&#xff0c;医疗行业作为关乎国计民生的关键领域&#xff0c;正面临着前所未有的挑战与机遇。一方面&#xff0c;传统医疗模式在应对海量医疗数据的处理、复…...

查找路由器的管理后台ip【通用找IP】

需求&#xff1a; 刚刚搞了个【小米】路由器&#xff0c;我想进路由的管理后台&#xff0c;提示&#xff1a;安装xx的路由管家&#xff0c;我不想安装 但是无法找到这个管理后台。 而且我是用这个路由作为中继&#xff0c;那么这个路由的ip就会经常更换 尝试通过网上搜索引擎来…...

Nginx:Stream模块

什么是 Stream 模块? Stream 模块 是 Nginx 的一个核心模块,专为处理非 HTTP 协议的流量(TCP 和 UDP 流量)而设计。它可以用来负载均衡和代理 TCP 或 UDP 连接,适用于多种应用场景,如: 数据库集群(MySQL、PostgreSQL 等)邮件服务器(SMTP、IMAP、POP3)游戏服务器VoI…...

C++语言的并发编程

C语言的并发编程 引言 随着计算机技术的飞速发展&#xff0c;尤其是多核处理器的普及&#xff0c;如何高效地利用计算机资源成为了一个重要话题。在这个背景下&#xff0c;并发编程已经成为了一种必须掌握的技能。C作为一种强大的编程语言&#xff0c;也提供了丰富的工具和库…...

Boost.Asio 同步读写及客户端 - 服务器实现详解

Boost.Asio 同步读写及客户端 - 服务器实现详解 参考文献 Boost.Asio 官方文档学习资料来源: 参考网址 一、引言 Boost.Asio作为一个强大的跨平台网络编程库&#xff0c;为开发者提供了丰富的网络操作接口。在之前的学习中&#xff0c;我们已接触到其同步读写的API函数&…...

机器人技术:ModbusTCP转CCLINKIE网关应用

在当今自动化生产与智能制造领域&#xff0c;ModbusTCP转CC-LinkIE网关KJ-MTCPZ-CCIES的应用正日益成为提升生产效率、实现设备间高效通信的重要技术手段。这一转换技术不仅打破了不同通信协议间的壁垒&#xff0c;还为机器人产品的应用提供了更为广阔的舞台。ModbusTCP作为一种…...

CSS——24.实战技能网导航栏 hove状态

<!DOCTYPE html> <html><head><meta charset"UTF-8"><title>技能网导航hover状态</title><style type"text/css">nav{width: 1000px;height: 40px;background:#363636;margin: 100px auto;}nav div{width: 100p…...

计算机视觉目标检测-DETR网络

目录 摘要abstractDETR目标检测网络详解二分图匹配和损失函数 DETR总结总结 摘要 DETR&#xff08;DEtection TRansformer&#xff09;是由Facebook AI提出的一种基于Transformer架构的端到端目标检测方法。它通过将目标检测建模为集合预测问题&#xff0c;摒弃了锚框设计和非…...

【计算机网络】课程 实验五 静态路由配置

实验五 静态路由配置 一、实验目的 理解静态路由的工作原理&#xff0c;掌握如何配置静态路由。 二、实验分析与设计 【背景描述】 假设校园网分为 2 个区域&#xff0c;每个区域内使用 1 台路由器连接 2 个子网&#xff0c; 现要在路由器上 做适当配置&#xff0c;实现校…...

FPGA车牌识别

基于FPGA的车牌识别主要包含以下几个步骤&#xff1a;图像采集、颜色空间转换、边缘检测、形态学处理&#xff08;腐蚀和膨胀&#xff09;、特征值提取、模板匹配、结果显示。先用matlab对原理进行仿真&#xff0c;后用vivado和modelsim进行设计和仿真。 一、1.图像采集采用ov…...

最好用的图文识别OCR -- PaddleOCR(2) 提高推理效率(PPOCR模型转ONNX模型进行推理)

在实际推理过程中&#xff0c;使用 PaddleOCR 模型时效率较慢&#xff0c;经测试每张图片的检测与识别平均耗时超过 5 秒&#xff0c;这在需要大规模自动化处理的场景中无法满足需求。为此&#xff0c;我尝试将 PaddleOCR 模型转换为 ONNX 格式进行推理&#xff0c;以提升效率。…...

STM32-笔记39-SPI-W25Q128

一、什么是SPI&#xff1f; SPI是串行外设接口&#xff08;Serial Peripheral Interface&#xff09;的缩写&#xff0c;是一种高速的&#xff0c;全双工&#xff0c;同步的通信总线&#xff0c;并且 在芯片的管脚上只占用四根线&#xff0c;节约了芯片的管脚&#xff0c;同时为…...

反规范化带来的数据不一致问题的解决方案

在数据库设计中&#xff0c;规范化&#xff08;Normalization&#xff09;和反规范化&#xff08;Denormalization&#xff09;是两个相互对立但又不可或缺的概念。规范化旨在消除数据冗余&#xff0c;确保数据的一致性和准确性&#xff0c;但可能会降低查询效率。相反&#xf…...

依赖注入学习

1.介绍 依赖注入&#xff08;Dependency Injection, DI&#xff09;是一种软件设计模式&#xff0c;用于实现类与其依赖项之间的解耦。它的核心思想是&#xff0c;将类所依赖的对象通过外部注入的方式传递给它&#xff0c;而不是在类内部自行创建依赖对象。通过这种方式&#…...

ESP32-C3 AT WiFi AP 启 TCP Server 被动接收模式 + BLE 共存

TCP 被动接收模式&#xff0c;每次发的数据会先存到缓冲区&#xff0c;参见&#xff1a;ATCIPRECVTYPE 指令说明。 即每包数据不会实时报告 IPD 接收情况&#xff0c;如果需要查询缓冲区的数据&#xff0c;先用 ATCIPRECVLEN? 指令查询被动接收模式下套接字数据的长度 。获取…...

git tag

文章目录 1.简介2.格式3.选项4.示例参考文献 1.简介 同大多数 VCS 一样&#xff0c;Git 也可以对某一时间点的版本打上标签&#xff0c;用于版本的发布管理。 一个版本发布时&#xff0c;我们可以为当前版本打上类似于 v.1.0.1、v.1.0.2 这样的 Tag。一个 Tag 指向一个 Commi…...

rabbitmq的三个交换机及简单使用

提前说一下&#xff0c;创建队列&#xff0c;交换机&#xff0c;绑定交换机和队列都是在生产者。消费者只负责监听就行了&#xff0c;不用配其他的。 完成这个场景需要两个服务哦。 1直连交换机-生产者的代码。 在配置类中创建队列&#xff0c;交换机&#xff0c;绑定交换机…...

TCP 如何获取端口信息

注&#xff1a;本文为 “TCP 如何获取端口信息” 相关讨论摘录。 机翻&#xff0c;未校。 How TCP Gets Port Information TCP 如何获取端口信息 asked Nov 10, 2024 at 19:57 user15503745 API Call for Connection API 调用以建立连接 Before the app can send data d…...

HashMap和HashTable的区别

1、HashMap是线程不安全的&#xff0c;HashTable是线程安全的 HashMap&#xff1a;Fail-fast 机制。表示快速失败&#xff0c;在集合遍历过程中&#xff0c;一旦发现容器中的数据被修改了&#xff0c;会立刻抛出ConcurrentModificationException异常&#xff0c;从而导致遍历失…...

USB 传输技术 OTG(On-The-Go)极简理解

OTG 极简理解 OTG&#xff0c;全称为 On-The-Go&#xff0c;它是一种 USB 传输技术 OTG 的主要应用于不同的设备或移动设备间的联接&#xff0c;进行数据交换 OTG 允许在没有电脑作为中转站的情况下&#xff0c;实现设备间的数据传送以及不同设备间的互相连接 在手机中&…...

SpringBoot插件

SpringBoot的插件机制是其强大灵活性的重要体现&#xff0c;它允许开发人员将应用程序的不同功能模块打包为独立的插件&#xff0c;并可以动态地加载和卸载这些插件。以下是对SpringBoot插件机制的详细解析&#xff1a; 一、插件机制的概念 插件机制是一种软件开发方法&#…...

【开发环境搭建篇】Visual Studio 2022 安装和使用

本文收录于 《C编程入门》专栏&#xff0c;从零基础开始&#xff0c;介绍C编程入门相关的内容&#xff0c;欢迎关注&#xff0c;谢谢&#xff01; 文章目录 一、前言二、下载三、安装四、使用五、总结 一、前言 本文介绍如何在Windows环境下安装Visual Studio 2022。 什么是Vi…...