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

【Rust基础③】方法method、泛型与特征

文章目录

  • 6 方法 Method
    • 6.1 定义方法
      • self、&self 和 &mut self
    • 6.2 自动引用和解引用
    • 6.3 关联函数
  • 7 泛型和特征
    • 7.1 泛型 Generics
      • 7.1.1 结构体中使用泛型
      • 7.1.2 枚举中使用泛型
      • 7.1.3 方法中使用泛型
        • 为具体的泛型类型实现方法
      • 7.1.4 const 泛型
    • 7.2 特征 Trait
      • 7.2.1 为类型实现特征
      • 7.2.2 使用特征作为函数参数
      • 7.2.3 特征约束(trait bound)
        • 多重约束
        • where约束
      • 7.2.4 函数返回中的impl trait
      • 7.2.5 两个综合例子
        • 为自定义类型实现 `+` 操作
        • 自定义类型的打印输出
    • 7.3 特征对象

6 方法 Method

Rust中,方法往往和对象成对出现:object.method()。Rust 的方法往往跟结构体、枚举、特征(Trait)一起使用

6.1 定义方法

Rust 使用 impl 来定义方法,例如以下代码:

struct Circle {x: f64,y: f64,radius: f64,
}impl Circle {// new是Circle的关联函数,因为它的第一个参数不是self,且new并不是关键字// 这种方法往往用于初始化当前结构体的实例fn new(x: f64, y: f64, radius: f64) -> Circle {Circle {x: x,y: y,radius: radius,}}// Circle的方法,&self表示借用当前的Circle结构体fn area(&self) -> f64 {std::f64::consts::PI * (self.radius * self.radius)}
}

注:Rust 的对象定义和方法定义是分离的,这种数据和使用分离的方式,会给予使用者极高的灵活度。

self、&self 和 &mut self

在一个 impl 块内,Self 指代被实现方法的结构体类型,self 指代此类型的实例,即为哪个结构体实现方法,那么 self 就是指代哪个结构体的实例。self 依然有所有权的概念:

  • self 表示 Rectangle 的所有权转移到该方法中,这种形式用的较少
  • &self 表示该方法对 Rectangle 的不可变借用
  • &mut self 表示可变借用

self 的使用就跟函数参数一样,要严格遵守 Rust 的所有权规则。使用方法代替函数有以下好处:

  • 不用在函数签名中重复书写 self 对应的类型
  • 代码的组织性和内聚性更强,对于代码维护和阅读来说,好处巨大

在 Rust 中,允许方法名跟结构体的字段名相同,往往适用于实现 getter 访问器:

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());
}

用这种方式,我们可以把 Rectangle 的字段设置为私有属性,只需把它的 newwidth 方法设置为公开可见,那么用户就可以创建一个矩形,同时通过访问器 rect1.width() 方法来获取矩形的宽度,因为 width 字段是私有的,当用户访问 rect1.width 字段时,就会报错。注意在此例中,Self 指代的就是被实现方法的结构体 Rectangle

6.2 自动引用和解引用

在 C/C++ 语言中,有两个不同的运算符来调用方法:. 直接在对象上调用方法,而 -> 在一个对象的指针上调用方法,这时需要先解引用指针。换句话说,如果 object 是一个指针,那么 object->something()(*object).something() 是一样的。

Rust 并没有一个与 -> 等效的运算符;相反,Rust 有一个叫 自动引用和解引用的功能。方法调用是 Rust 中少数几个拥有这种行为的地方。他是这样工作的:当使用 object.something() 调用方法时,Rust 会自动为 object 添加 &&mut* 以便使 object 与方法签名匹配。也就是说,这些代码是等价的:

p1.distance(&p2);
(&p1).distance(&p2);

第一行看起来简洁的多。这种自动引用的行为之所以有效,是因为方法有一个明确的接收者———— self 的类型。在给出接收者和方法名的前提下,Rust 可以明确地计算出方法是仅仅读取(&self),做出修改(&mut self)或者是获取所有权(self)。事实上,Rust 对方法接收者的隐式借用让所有权在实践中更友好。

6.3 关联函数

思考一个问题,如何为一个结构体定义一个构造器方法?也就是接受几个参数,然后构造并返回该结构体的实例。其实答案在开头的代码片段中就给出了,很简单,参数中不包含 self 即可。

这种定义在 impl 中且没有 self 的函数被称之为关联函数: 因为它没有 self,不能用 f.read() 的形式调用,因此它是一个函数而不是方法,它又在 impl 中,与结构体紧密关联,因此称为关联函数。

在之前的代码中,我们已经多次使用过关联函数,例如 String::from,用于创建一个动态字符串。

impl Rectangle {fn new(w: u32, h: u32) -> Rectangle {Rectangle { width: w, height: h }}
}

Rust 中有一个约定俗成的规则,使用 new 来作为构造器的名称,出于设计上的考虑,Rust 特地没有用 new 作为关键字

因为是函数,所以不能用 . 的方式来调用,我们需要用 :: 来调用,例如 let sq = Rectangle::new(3, 3);。这个方法位于结构体的命名空间中::: 语法用于关联函数和模块创建的命名空间。

Rust 允许我们为一个结构体定义多个 impl 块,目的是提供更多的灵活性和代码组织性,例如当方法多了后,可以把相关的方法组织在同一个 impl 块中,那么就可以形成多个 impl 块,各自完成一块儿目标。

除了结构体,还可以为枚举、特征(trait)实现方法,如:

#![allow(unused)]
enum Message {Quit,Move { x: i32, y: i32 },Write(String),ChangeColor(i32, i32, i32),
}impl Message {fn call(&self) {// 在这里定义方法体}
}fn main() {let m = Message::Write(String::from("hello"));m.call();
}

7 泛型和特征

7.1 泛型 Generics

使用泛型参数,有一个先决条件,必需在使用前对其进行声明:

fn largest<T>(list: &[T]) -> T {}

可以这样理解这个函数定义:函数 largest 有泛型类型 T,它有个参数 list,其类型是元素为 T 的数组切片,最后,该函数返回值的类型也是 T

7.1.1 结构体中使用泛型

结构体中的字段类型也可以用泛型来定义,下面代码定义了一个坐标点 Point,它可以存放任何类型的坐标值:

struct Point<T> {x: T,y: T,
}fn main() {let integer = Point { x: 5, y: 10 };let float = Point { x: 1.0, y: 4.0 };
}

这里有两点需要特别的注意:

  • 提前声明,跟泛型函数定义类似,首先我们在使用泛型参数之前必需要进行声明 Point<T>,接着就可以在结构体的字段类型中使用 T 来替代具体的类型
  • x 和 y 是相同的类型,若需要不同类型,将声明改为struct Point<T,U> { x: T, y: U, }

7.1.2 枚举中使用泛型

Option 枚举类型

enum Option<T> {Some(T),None,
}

Option<T> 是一个拥有泛型 T 的枚举类型,它第一个成员是 Some(T),存放了一个类型为 T 的值。可以在任何一个需要返回值的函数中,去使用 Option<T> 枚举类型来做为返回值,用于返回一个任意类型的值 Some(T),或者没有值 None

Result枚举类型

enum Result<T, E> {Ok(T),Err(E),
}

主要用于函数返回值,如果函数正常运行,则最后返回一个 Ok(T)T 是函数具体的返回值类型,如果函数异常运行,则返回一个 Err(E)E 是错误类型。例如打开一个文件:如果成功打开文件,则返回 Ok(std::fs::File),因此 T 对应的是 std::fs::File 类型;而当打开文件时出现问题时,返回 Err(std::io::Error)E 对应的就是 std::io::Error 类型。

7.1.3 方法中使用泛型

在结构体的方法中定义额外的泛型参数,就跟泛型函数一样:

struct Point<T, U> {x: T,y: U,
}impl<T, U> Point<T, U> {fn mixup<V, W>(self, other: Point<V, W>) -> Point<T, W> {Point {x: self.x,y: other.y,}}
}fn main() {let p1 = Point { x: 5, y: 10.4 };let p2 = Point { x: "Hello", y: 'c'};let p3 = p1.mixup(p2);println!("p3.x = {}, p3.y = {}", p3.x, p3.y);
}

这个例子中,T,U 是定义在结构体 Point 上的泛型参数,V,W 是单独定义在方法 mixup 上的泛型参数,可以理解为,一个是结构体泛型,一个是函数泛型。

为具体的泛型类型实现方法

对于 Point<T> 类型,你不仅能定义基于 T 的方法,还能针对特定的具体类型,进行方法定义:

impl Point<f32> {fn distance_from_origin(&self) -> f32 {(self.x.powi(2) + self.y.powi(2)).sqrt()}
}

这段代码意味着 Point<f32> 类型会有一个方法 distance_from_origin,而其他 T 不是 f32 类型的 Point<T> 实例则没有定义此方法。这个方法计算点实例与坐标(0.0, 0.0) 之间的距离,并使用了只能用于浮点型的数学运算符。

这样我们就能针对特定的泛型类型实现某个特定的方法,对于其它泛型类型则没有定义该方法

7.1.4 const 泛型

const 泛型,针对值的泛型,可以用于处理数组长度的问题:

fn display_array<T: std::fmt::Debug, const N: usize>(arr: [T; N]) {println!("{:?}", arr);
}
fn main() {let arr: [i32; 3] = [1, 2, 3];display_array(arr);let arr: [i32; 2] = [1, 2];display_array(arr);
}

如上所示,我们定义了一个类型为 [T; N] 的数组,其中 T 是一个基于类型的泛型参数,重点在于 N 这个泛型参数,它是一个基于值的泛型参数!因为它用来替代的是数组的长度。

注意的是需要对 T 加一个限制 std::fmt::Debug,该限制表明 T 可以用在 println!("{:?}", arr) 中,因为 {:?} 形式的格式化输出需要 arr 实现该特征。

N 就是 const 泛型,定义的语法是 const N: usize,表示 const 泛型 N ,它基于的值类型是 usize

Rust 通过在编译时进行泛型代码的 单态化(monomorphization)来保证效率。单态化是一个通过填充编译时使用的具体类型,将通用代码转换为特定代码的过程。

编译器所做的工作正好与我们创建泛型函数的步骤相反,编译器寻找所有泛型代码被调用的位置并针对具体类型生成代码。

7.2 特征 Trait

特征类似于其他语言中的接口,定义了一组可以被共享的行为,只要实现了特征,你就能使用这组行为

7.2.1 为类型实现特征

因为特征只定义行为看起来是什么样的,因此我们需要为类型实现具体的特征,定义行为具体是怎么样的。

首先来为 PostWeibo 实现 Summary 特征:

//特征定义,也可以在特征中定义具有默认实现的方法
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)}
}pub struct Weibo {pub username: String,pub content: String
}impl Summary for Weibo {fn summarize(&self) -> String {format!("{}发表了微博{}", self.username, self.content)}
}

实现特征的语法与为结构体、枚举实现方法很像:impl Summary for Post,读作“为 Post 类型实现 Summary 特征”,然后在 impl 的花括号中实现该特征的具体方法。

关于特征实现与定义的位置,有一条非常重要的原则:如果你想要为类型 A 实现特征 T,那么 A 或者 T 至少有一个是在当前作用域中定义的!

7.2.2 使用特征作为函数参数

定义一个函数,使用特征作为函数参数:

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

impl Summary,它的意思是 实现了Summary特征item 参数。

你可以使用任何实现了 Summary 特征的类型作为该函数的参数,同时在函数体内,还可以调用该特征的方法,例如 summarize 方法。具体的说,可以传递 PostWeibo 的实例来作为参数,而其它类如 String 或者 i32 的类型则不能用做该函数的参数,因为它们没有实现 Summary 特征。

7.2.3 特征约束(trait bound)

虽然 impl Trait 这种语法非常好理解,但是实际上它只是一个语法糖:

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

真正的完整书写形式如上所述,形如 T: Summary 被称为特征约束

在简单的场景下 impl Trait 这种语法糖就足够使用,但是对于复杂的场景,特征约束可以让我们拥有更大的灵活性和语法表现能力,例如一个函数接受两个 impl Summary 的参数:

pub fn notify(item1: &impl Summary, item2: &impl Summary) {}

如果函数两个参数是不同的类型,那么上面的方法很好,只要这两个类型都实现了 Summary 特征即可。但是如果我们想要强制函数的两个参数是同一类型呢?上面的语法就无法做到这种限制,此时我们只能使特征约束来实现:

pub fn notify<T: Summary>(item1: &T, item2: &T) {}

泛型类型 T 说明了 item1item2 必须拥有同样的类型,同时 T: Summary 说明了 T 必须实现 Summary 特征。

多重约束
pub fn notify<T: Summary + Display>(item: &T) {}

通过这两个特征,就可以使用 item.summarize 方法,以及通过 println!("{}", item) 来格式化输出 item

where约束

当特征约束变得很多时,函数的签名将变得很复杂:

fn some_function<T: Display + Clone, U: Clone + Debug>(t: &T, u: &U) -> i32 {}

严格来说,上面的例子还是不够复杂,但是我们还是能对其做一些形式上的改进,通过 where

fn some_function<T, U>(t: &T, u: &U) -> i32where T: Display + Clone,U: Clone + Debug
{}

7.2.4 函数返回中的impl trait

可以通过 impl Trait 来说明一个函数返回了一个类型,该类型实现了某个特征:

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

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

当函数返回的真实类型非常复杂,不知道该怎么声明时(毕竟 Rust 要求你必须标出所有的类型),此时就可以用 impl Trait 的方式简单返回。例如,闭包和迭代器就是很复杂,只有编译器才知道那玩意的真实类型,如果让你写出来它们的具体类型,估计内心有一万只草泥马奔腾,好在你可以用 impl Iterator 来告诉调用者,返回了一个迭代器,因为所有迭代器都会实现 Iterator 特征。

7.2.5 两个综合例子

为自定义类型实现 + 操作

在 Rust 中除了数值类型的加法,String 也可以做加法,因为 Rust 为该类型实现了 std::ops::Add 特征,同理,如果我们为自定义类型实现了该特征,那就可以自己实现 Point1 + Point2 的操作:

use std::ops::Add;// 为Point结构体派生Debug特征,用于格式化输出
#[derive(Debug)]
struct Point<T: Add<T, Output = T>> { //限制类型T必须实现了Add特征,否则无法进行+操作。x: T,y: T,
}impl<T: Add<T, Output = T>> Add for Point<T> {type Output = Point<T>;fn add(self, p: Point<T>) -> Point<T> {Point{x: self.x + p.x,y: self.y + p.y,}}
}fn add<T: Add<T, Output=T>>(a:T, b:T) -> T {a + b
}fn main() {let p1 = Point{x: 1.1f32, y: 1.1f32};let p2 = Point{x: 2.1f32, y: 2.1f32};println!("{:?}", add(p1, p2));let p3 = Point{x: 1i32, y: 1i32};let p4 = Point{x: 2i32, y: 2i32};println!("{:?}", add(p3, p4));
}
自定义类型的打印输出

在开发过程中,往往只要使用 #[derive(Debug)] 对我们的自定义类型进行标注,即可实现打印输出的功能:

#[derive(Debug)]
struct Point{x: i32,y: i32
}
fn main() {let p = Point{x:3,y:3};println!("{:?}",p);
}

但是在实际项目中,往往需要对我们的自定义类型进行自定义的格式化输出,以让用户更好的阅读理解我们的类型,此时就要为自定义类型实现 std::fmt::Display 特征:

#![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,}}
}fn main() {let f6 = File::new("f6.txt");//...println!("{:?}", f6);println!("{}", f6);
}

7.3 特征对象

可以通过 & 引用或者 Box<T> 智能指针的方式来创建特征对象。

trait Draw {fn draw(&self) -> String;
}impl Draw for u8 {fn draw(&self) -> String {format!("u8: {}", *self)}
}impl Draw for f64 {fn draw(&self) -> String {format!("f64: {}", *self)}
}// 若 T 实现了 Draw 特征, 则调用该函数时传入的 Box<T> 可以被隐式转换成函数参数签名中的 Box<dyn Draw>
fn draw1(x: Box<dyn Draw>) {// 由于实现了 Deref 特征,Box 智能指针会自动解引用为它所包裹的值,然后调用该值对应的类型上定义的 `draw` 方法x.draw();
}fn draw2(x: &dyn Draw) {x.draw();
}fn main() {let x = 1.1f64;// do_something(&x);let y = 8u8;// x 和 y 的类型 T 都实现了 `Draw` 特征,因为 Box<T> 可以在函数调用时隐式地被转换为特征对象 Box<dyn Draw> // 基于 x 的值创建一个 Box<f64> 类型的智能指针,指针指向的数据被放置在了堆上draw1(Box::new(x));// 基于 y 的值创建一个 Box<u8> 类型的智能指针draw1(Box::new(y));draw2(&x);draw2(&y);
}

上面代码,有几个非常重要的点:

  • draw1 函数的参数是 Box<dyn Draw> 形式的特征对象,该特征对象是通过 Box::new(x) 的方式创建的
  • draw2 函数的参数是 &dyn Draw 形式的特征对象,该特征对象是通过 &x 的方式创建的
  • dyn 关键字只用在特征对象的类型声明上,在创建时无需使用 dyn

因此,可以使用特征对象来代表泛型或具体的类型。

泛型是在编译期完成处理的:编译器会为每一个泛型参数对应的具体类型生成一份代码,这种方式是静态分发(static dispatch),因为是在编译期完成的,对于运行期性能完全没有任何影响。

与静态分发相对应的是动态分发(dynamic dispatch),在这种情况下,直到运行时,才能确定需要调用什么方法。之前代码中的关键字 dyn 正是在强调这一“动态”的特点。当使用特征对象时,Rust 必须使用动态分发。

特征对象特点:

  • 特征对象大小不固定(对于不同的类型可以实现相同特征)
  • 几乎总是使用特征对象的引用方式,如 &dyn DrawBox<dyn Draw>(引用大小是固定的,占用两个指针大小:ptr-指向实现了特征的具体类型的实例/vptr-指向虚表vtable)

在Rust中,一般Self代表类型,self指代当前实例对象。

不是所有特征都能拥有特征对象,只有对象安全的特征才行。当一个特征的所有方法都有如下属性时,它的对象才是安全的:

  • 方法的返回类型不能是 Self
  • 方法没有任何泛型参数

相关文章:

【Rust基础③】方法method、泛型与特征

文章目录 6 方法 Method6.1 定义方法self、&self 和 &mut self 6.2 自动引用和解引用6.3 关联函数 7 泛型和特征7.1 泛型 Generics7.1.1 结构体中使用泛型7.1.2 枚举中使用泛型7.1.3 方法中使用泛型为具体的泛型类型实现方法 7.1.4 const 泛型 7.2 特征 Trait7.2.1 为类…...

48.排列问题求解

思路分析&#xff1a;通过为每一队分配一个id&#xff0c;join条件要求t1.num < t2.num实现相同两队只比一次 代码实现&#xff1a; with t as (SELECT team_name,caseteam_nameWHEN 勇士 then 1WHEN 湖人 then 2WHEN 灰熊 then 3else 4end numFROM team )SELECT t1.team_…...

18.(开发工具篇Gitlab)Git如何回退到指定版本

首先: 使用git log命令查看提交历史,找到想要回退的版本的commit id. 使用git reset命令 第一步:git reset --hard 命令是强制回到某一个版本。执行后本地工程回退到该版本。 第二步:利用git push -f命令强制推到远程 如下所示: 优点:干净利落,回滚后完全回到最初状态…...

IDEA初始配置

1. 详细设置 安装完IDEA之后的简单配置。 1.1 如何打开详细配置界面 1、显示工具栏 2、选择详细配置菜单或按钮 1.2 系统设置 1、默认启动项目配置 启动IDEA时&#xff0c;默认自动打开上次开发的项目&#xff1f;还是自己选择&#xff1f; 如果去掉Reopen projects on …...

WM_COPYDATA传回返回值的一个方案

方案背景 适应场景&#xff0c;通过WM_COPYDATA进行进程间通信时&#xff0c;SendMessage不能返回自定义的数据&#xff0c;由此想到以下思路解决这个问题 A进程使用VirtualAlloc分配一块内存&#xff0c;通过某种方式将此地址以及A进程ID传给另一个进程B B进程使用OpenProce…...

【日常业务开发】接口性能优化

【日常业务开发】接口性能优化 缓存本地缓存分布式缓存 数据库分库分表SQL 优化 业务程序并行化异步化池化技术预先计算事务粒度批量读写锁的粒度尽快return上下文传递空间换时间集合空间大小 缓存 本地缓存 本地缓存&#xff0c;最大的优点是应用和cache同一个进程内部&…...

Android 10.0 禁止弹出系统simlock的锁卡弹窗功能实现

1.前言 在10.0的系统开发中,在一款产品中,需要实现simlock锁卡功能,在系统实现锁卡功能以后,在开机的过程中,或者是在插入sim卡 后,当系统检测到是禁用的sim卡后,就会弹出simlock锁卡弹窗,要求输入puk 解锁密码,功能需求禁用这个弹窗,所以就需要看是 哪里弹的,禁用…...

VulnHub lazysysadmin

一、信息收集 1.nmap扫描开发端口 开放了&#xff1a;22、80、445 访问80端口&#xff0c;没有发现什么有价值的信息 2.扫描共享文件 enum4linux--扫描共享文件 使用&#xff1a; enum4linux 192.168.103.182windows访问共享文件 \\192.168.103.182\文件夹名称信息收集&…...

ppt怎么压缩到10m以内?分享ppt缩小方法

在日常工作中&#xff0c;我们常常需要制作和分享PowerPoint演示文稿&#xff0c;然而&#xff0c;有时候文稿中的图片、视频等元素会导致文件过大&#xff0c;无法在电子邮件或其他平台上顺利传输。为了将PPT文件压缩到10M以内&#xff0c;我们可以使用一些专门的文件压缩工具…...

智能警用装备管理系统-科技赋能警务

警用物资装备管理系统&#xff08;智装备DW-S304&#xff09;是依托互云计算、大数据、RFID技术、数据库技术、AI、视频分析技术对警用装备进行统一管理、分析的信息化、智能化、规范化的系统。 &#xff08;1&#xff09;感知智能化 装备感知是整个方案的基础&#xff0c;本方…...

攻防千层饼

近年来&#xff0c;网络安全领域正在经历一场不断升级的攻防对抗&#xff0c;这场攻防已经不再局限于传统的攻击与防御模式&#xff0c;攻击者和防守者都已经越发熟练&#xff0c;对于传统攻防手法了如指掌。 在这个背景下&#xff0c;攻击者必须不断寻求创新的途径&#xff0…...

组件封装使用?

组件封装是指在软件开发中&#xff0c;将功能代码或数据封装成一个独立的、可重用的模块或组件。这种封装可以使得代码更加模块化、可维护性和可重用性。在许多编程语言和开发框架中&#xff0c;都有不同的方式来实现组件封装。 以下是一些常见的组件封装方法和技巧&#xff1…...

2.3 初探Hadoop世界

文章目录 零、学习目标一、导入新课二、新课讲解&#xff08;一&#xff09;Hadoop的前世今生1、Google处理大数据三大技术2、Hadoop如何诞生3、Hadoop主要发展历程 &#xff08;二&#xff09;Hadoop的优势1、扩容能力强2、成本低3、高效率4、可靠性5、高容错性 &#xff08;三…...

Flutter笔记:发布一个电商中文货币显示插件Money Display

Flutter笔记 电商中文货币显示插件 Money Display 作者&#xff1a;李俊才 &#xff08;jcLee95&#xff09;&#xff1a;https://blog.csdn.net/qq_28550263 邮箱 &#xff1a;291148484163.com 本文地址&#xff1a;https://blog.csdn.net/qq_28550263/article/details/1338…...

解密zkLogin:探索前沿的Sui身份验证解决方案

由于钱包复杂性导致的新用户入门障碍是区块链中一个长期存在的问题&#xff0c;而zkLogin是其简单的解决方案。通过使用前沿的密码学和技术&#xff0c;zkLogin既优雅又复杂。本文深入探讨了zkLogin的工作原理&#xff0c;涵盖了用户和开发者的安全性方面&#xff0c;并解释了S…...

js构造函数

构造函数 通过 new 函数名 来实例化对象的函数叫构造函数。 任何的函数都可以作为构造函数存在。之所以有构造函数与普通函数之分&#xff0c;主要从功能上进行区别的&#xff0c;构造函数的主要 功能为 初始化对象&#xff0c;特点是和new 一起使用。new就是在创建对象&#x…...

性能测试-redis常见问题

缓存击穿、缓存穿透、缓存雪崩 缓存雪崩 解决办法 1.设置缓存失效时间&#xff0c;不要在同一时间 2.redis集群部署 3.不设置缓存设置时间 4.定时刷缓存的时间 缓存穿透 请求不管返回什么数据都返回给redis对参数合法器进行验证&#xff0c;不合法的时候直接过滤掉使用布…...

预测:2024 年将是互联网永远改变的一年。

人工智能的下一步发展将彻底改变互联网的各个方面。 如果你真的认为人工智能只是另一个炒作周期&#xff0c;那么你就会迎来新的觉醒。 以下是即将发生的事情&#xff1a; 1. 自主待办事项列表/代理&#xff1a;无需人工干预即可执行任务的人工智能。 这些代理将发送您的电子邮…...

Vue2 与 React 的区别

【5年以上前端】Vue 和 React 的区别看这里 - 知乎 vue和react的区别_vue react-CSDN博客 Vue 和 React 有什么不同&#xff1f;_vue和react区别-CSDN博客 1、相同点&#xff1a; ① 都使用了虚拟 DOM&#xff1b; ② 组件化开发&#xff1b; ③ 都是单向数据流&#xff…...

【AI视野·今日Robot 机器人论文速览 第五十一期】Tue, 10 Oct 2023

AI视野今日CS.Robotics 机器人学论文速览 Tue, 10 Oct 2023 Totally 54 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers On Multi-Fidelity Impedance Tuning for Human-Robot Cooperative Manipulation Authors Ethan Lau, Vaibhav Srivastava, Sh…...

零经验想跳槽转行网络安全,需要准备什么?

最近在后台看到很多私信都是有关转行网络安全的问题&#xff0c;目前咨询最多的都是&#xff1a;觉得现在的工作没有发展空间&#xff0c;替代性强&#xff0c;工资低&#xff0c;想跳槽转行网络安全。其中&#xff0c;他们主要关心的是&#xff1a;没有经验怎么学习&#xff1…...

Rust-是否使用Rc<T>

Rust的所有权机制&#xff0c;数据允许通过借用的方式&#xff0c;在函数的上下文中传递数据。如果离开数据作用的有效范围&#xff0c;这个借用就会失效&#xff0c;编译就会报错。这也是我们不会将借用(引用&#xff09;作为函数的返回值的原因。下面的代码编译失败。 fn cr…...

论文解析——一种面向Chiplet互连的高效传输协议设计与实现

作者及发刊详情 熊国杰, 张津铭, 贺光辉. 一种面向Chiplet互连的高效传输协议设计与实现[J]. 计算机工程与科学, 2023, 45(08): 1339-1346.XIONG Guo-jie, ZHANG Jin-ming, HE Guang-hui. Design and implementation of an efficient transmission protocol for Chiplet inter…...

svo2.0 svo pro 编译运行

sudo apt-get install python-catkin-tools python-vcstool unable to locate python-vcstool 添加ros源 然后sudo apt update 依赖库下载&#xff0c;查看dependencies.yaml文件&#xff1a; 逐个clone到src目录下即可 dbow2_catkin 编译出错&#xff1a; 把https://gi…...

微信小程序前端生成动态海报图

//页面显示<canvas id"myCanvas" type"2d" style" width: 700rpx; height: 600rpx;" />onShareShow(e){var that this;let user_id wx.getStorageSync(user_id);let sharePicUrl wx.getStorageSync(sharePicUrl);if(app.isBlank(user_i…...

SQL如何导入数据以及第一次上机作业

如何导入excel数据 首先得学会导入数据 使用excel格式不需要改成其它格式&#xff08;如csv&#xff0c;txt&#xff09;&#xff0c;因为你改了到时候还是会报错&#xff08;实践过使用Sum统计总数一直说我数据格式有问题&#xff09; 首先右键TSGL数据库->任务->导入数…...

数据结构-----红黑树简介

目录 前言 1.什么是红黑树&#xff1f; 2.为什么需要红黑树&#xff1f;&#xff08;与AVL树对比&#xff09; 3.红黑树的特性 前言 在此之前我们学习过了二叉排序树和平衡二叉树&#xff08;AVL树&#xff09;&#xff0c;这两种树都是属于搜索树的一种&#xff0c;那么今天…...

哈佛教授因果推断力作:《Causal Inference: What If 》pdf下载

因果推断是一项复杂的科学任务&#xff0c;它依赖于多个来源的三角互证和各种方法论方法的应用&#xff0c;是用于解释分析的强大建模工具&#xff0c;同时也是机器学习领域的热门研究方向之一。 今天我要给大家推荐的这本书&#xff0c;正是因果推断领域必读的入门秘籍&#…...

Drecom 的《Eternal Crypt - Wizardry BC -》加入 The Sandbox 啦!

经典 “Wizardry” 游戏系列的新区块链迭代将通过全球合作拓展 Web3 游戏宇宙。 我们非常高兴地宣布&#xff0c;沙盒游戏公司与富有远见的传奇游戏《Wizardry》系列创造者 Drecom 将建立充满活力的合作伙伴关系。我们将共同推出《Eternal Crypt - Wizardry BC -》&#xff0c…...

外贸网站流量下降可能是这五点原因造成的

随着互联网的发展&#xff0c;企业开始重视网站优化&#xff0c;越来越多的人开始从事网站优化工作&#xff0c;然而真正做起来&#xff0c;很多站长朋友并非一帆风顺&#xff0c;往往越到很多问题&#xff0c;比如外贸网站流量出现异常下降情况&#xff0c;但很多时候在遇到外…...

怎么做科技小制作视频网站/微信小程序

文章目录数学符号设变量时常用的希腊字母大小关系分数开方同余一般符号二项式符号上下添加额外信息上标符号上下标上下划线箭头集合省略号矩阵小括号形式中括号形式行列式带省略号的形式带横线或竖线分隔的形式逻辑运算分支公式间距设置颜色更多参考End数学符号 设变量时常用的…...

我做网站了 圆通/关键字挖掘爱站网

日萌社 人工智能AI&#xff1a;Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战&#xff08;不定时更新&#xff09; 1.nn.BatchNorm1d(num_features)1.对小批量(mini-batch)的2d或3d输入进行批标准化(Batch Normalization)操作2.num_features&#xff1a;来自期望输…...

淘客网站如何做推广/seo招聘要求

1、将所有的.enw文件放在一个文件夹中&#xff1b; 2、利用dos命令copy *.enw new.enw将该文件夹中所有的.enw文件合并为一个new.enw文件&#xff1b; 3、用notepad打开&#xff0c;先用<br>%全部替换%&#xff0c;再用<br>%0全部替换%0&#xff0c;然后把文件另…...

做网站公司职员工资/百度重庆营销中心

树状数组可以扩充到二维。 问题&#xff1a;一个由数字构成的大矩阵&#xff0c;能进行两种操作 1) 对矩阵里的某个数加上一个整数&#xff08;可正可负&#xff09; 2) 查询某个子矩阵里所有数字的和,要求对每次查询&#xff0c;输出结果。 一维树状数组很容易扩展到二维&…...

江西学校网站建设/蚌埠seo外包

前言 在国内编译Spark项目需要从Maven源下载很多依赖包&#xff0c;官方源在国内大环境下的下载速度大家都懂得&#xff0c;那个煎熬啊&#xff0c;简直是浪费生命。 如果你的下载速度很快&#xff0c;你现在就可以无视这篇文章了。 阿里云给国内开发者提供了一个非常的良心的服…...

wordpress coming soon/软文推广经典案例

lftp是linux下的一款ftp工具&#xff0c;lftp虽然没有界面&#xff0c;但是安装简单&#xff0c;使用方便&#xff0c;功能也极为强大。1. 安装。在debian系下安装软件太简单了-_-!$ apt-get install lftp2. 显示远端文件列表:~> ls3. 切换远端目录&#xff1a;~> cd 目录…...