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

网址大全网站做的好的有哪些网站

网址大全网站,做的好的有哪些网站,视频营销案例,wordpress弹出聊天系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学…系列文章目录 【跟小嘉学 Rust 编程】一、Rust 编程基础 【跟小嘉学 Rust 编程】二、Rust 包管理工具使用 【跟小嘉学 Rust 编程】三、Rust 的基本程序概念 【跟小嘉学 Rust 编程】四、理解 Rust 的所有权概念 【跟小嘉学 Rust 编程】五、使用结构体关联结构化数据 【跟小嘉学 Rust 编程】六、枚举和模式匹配 【跟小嘉学 Rust 编程】七、使用包(Packages)、单元包(Crates)和模块(Module)来管理项目 【跟小嘉学 Rust 编程】八、常见的集合 【跟小嘉学 Rust 编程】九、错误处理(Error Handling) 【跟小嘉学 Rust 编程】十一、编写自动化测试 【跟小嘉学 Rust 编程】十二、构建一个命令行程序 【跟小嘉学 Rust 编程】十三、函数式语言特性迭代器和闭包 【跟小嘉学 Rust 编程】十四、关于 Cargo 和 Crates.io 【跟小嘉学 Rust 编程】十五、智能指针(Smart Point) 【跟小嘉学 Rust 编程】十六、无畏并发(Fearless Concurrency) 【跟小嘉学 Rust 编程】十七、面向对象语言特性 【跟小嘉学 Rust 编程】十八、模式匹配(Patterns and Matching) 【跟小嘉学 Rust 编程】十九、高级特性 文章目录 系列文章目录[TOC](文章目录) 前言一、 不安全 Rust1.1、不安全 Rust1.2、unsafe 关键字1.2.1、解引用裸指针(Dereference a raw pointer)、1.2.1.1、裸指针(raw pointer)1.2.1.2、基于引用创建裸指针1.2.1.3、内存地址创建裸指针1.2.1.4、使用 * 解引用1.2.1.5、基于指针指针创建裸指针1.2.1.6、总结 1.2.2、调用不安全的函数或方法1.2.2.1、调用不安全的函数或方法1.2.2.2、用安全抽象包裹 unsafe 代码1.2.2.3、FFI(Foreign Function Interface)1.2.2.4、ABI(Application Binary Interface)1.2.2.5、其他语言调用 Rust 函数 1.2.3、访问或修改可变静态变量1.2.4、实现不安全的Trait1.2.5、访问联合体的字段1.2.6、一些实用库 二、高级 Trait2.1、在 Trait定义中使用关联类型来指定占位类型2.2、关联类型与泛型区别2.3、默认泛型参数和运算符重载(operator overloading)2.4、默认泛型参数的主要场景2.5、完全限定语法(Fully Qualified Syntax)2.6、使用 supertrait 要求 trait 附带其他 trait 的功能2.7、使用 newtype 模式在外部类型上实现外部 trait 三、高级类型3.1、newtype模式3.2、使用类型别名创建类型同义词3.3、Never type3.3、动态大小 和 Size Trait3.3.1、动态大小3.3.2、Sized Trait3.3.4、?Sized Trait 约束 四、高级函数和闭包4.1、函数指针function pointer4.2、返回闭包 五、宏macro5.1、宏5.2、宏和函数的区别5.3、macro_rules! 声明宏5.4、用过程宏(procedural macros)为属性标记生成代码5.4.1、自定义 derive 宏5.4.2、属性宏5.4.3、函数宏 总结 前言 到目前为止我们已经学习了 Rust 之中最常用的部分本章节讲解如下特性 不安全 Rust如何选择退出 Rust 的某些保证并负责手动维护这些保证高级特征关联类型、默认类型参数、完全限定语法、超特征以及特征相关的newtype模式高级类型更多关于newtype模式、类型别名、never类型和动态大小类型的内容高级函数和闭包函数指针和返回闭包宏定义在编译时定义更多代码的方法 主要教材参考 《The Rust Programming Language》 一、 不安全 Rust 1.1、不安全 Rust 到目前为止我们讨论的所有代码在编译时强制执行了 Rust 的内存安全保证然而 Rust 内部隐藏着另一种语言它不强制执行这些内存安全保证它被称为不安全 Rust 和常规 Rust 一样工作但赋予我们额外的能力。 不安全 Rust 之所以存在是因为静态分析本质是保守的。当编译器试图确定代码是否支持这些保证时候拒绝一些有效的程序比接受一些无效的程序要好。虽然代码可能没有问题但如果 Rust 编译器没有足够的信息来确定它将拒绝代码。在这些情况下您可以使用不安全代码告诉编译器“相信我我知道我在做什么”。但是请注意使用不安全的 Rust 的风险由您自己承担如果不正确地使用不安全的代码可能会由于内存不安全而出现问题例如空指针解引用。 Rust 具有不安全另一面的原因是底层计算机硬件本质上是不安全的。如果 Rust 不允许你做不安全的操作你就不能完成某些任务。 Rust 需要允许您进行低级系统编程例如直接与操作系统交互甚至编写自己的操作系统。处理低级系统编程是该语言的目标之一让我们来探索一下不安全 Rust 可以做什么 以及如何做。 1.2、unsafe 关键字 要切换到不安全 Rust 使用 unsafe 关键字然后启动一个包含不安全代码的新块你可以在不安全的 Rust 中执行五个在安全 Rust 中无法执行的操作我们称之为不安全的超能力。 解引用裸指针调用不安全的函数或方法访问或修改可变静态变量实现不安全的Trait访问联合体的字段 1.2.1、解引用裸指针(Dereference a raw pointer)、 1.2.1.1、裸指针(raw pointer) 裸指针(raw pointer又称原生指针)在功能上跟引用类型同时也需要显式地注明可变性。但是又和引用有所不同裸指针形式如 * const T 和 *mut T分别代表了不可变和可变。 * 操作符可以用于解引用但是裸指针 * const T 中* 只是类型名称的一部分并没有解引用的含义。 至此我们已经学过三种类似指针的概念引用、 智能指针、裸指针。与前两者不同裸指针可以绕过 Rust 的借用规则可以同时拥有一个数据的可变、不可变指针甚至可以拥有多个可变的指针并不能保证指向合法的内存可以是null没有实现任何自动回收的(drop) 总之裸指针和 C指针非常像它需要以牺牲安全性为前提但是我们获得了更好的性能也可以跟其他语言和硬件打交道。 1.2.1.2、基于引用创建裸指针 范例基于引用创建裸指针 let mut num 5;let r1 num as *const i32; let r2 mut num as *mut i32;as 关键字可以用于强制类型转换我们这里将引用 mut / mut num 强制转换为 * const i32 / * mut i32 在这段代码里面并没有 unsafe 身影因为创建裸指针是安全的行为而解引用裸指针才是不安全的行为。 范例解引用裸指针 fn main() {let mut num 5;let r1 num as *const i32;unsafe {println!(r1 is: {}, *r1);} }1.2.1.3、内存地址创建裸指针 我们基于引用创建裸指针这种行为是很安全的但是接下来的方式就不安全了。 let address 0x012345usize; let r address as *const i32;这里是基于一个内存地址来创建裸指针这种行为相当危险试图使用任意的内存地址往往是一种未定义的行为(undefined behavior)因为该内存地址有可能存在值也有可能没有就算有值也大概率不是你需要的值。 同时编译器也有可能会优化这段代码会造成没有任何内存访问发送甚至程序还可能会发生段错误(segmentation fault)。总之你几乎没有好的理由像上面这样实现代码虽然它是可行的。 如果真的要使用内存地址也是类似下面的用法啊先取地址再使用而不是凭空捏造一个地址。 use std::{slice::from_raw_parts, str::from_utf8_unchecked};// 获取字符串的内存地址和长度 fn get_memory_location() - (usize, usize) {let string Hello World!;let pointer string.as_ptr() as usize;let length string.len();(pointer, length) }// 在指定的内存地址读取字符串 fn get_str_at_location(pointer: usize, length: usize) - static str {unsafe { from_utf8_unchecked(from_raw_parts(pointer as *const u8, length)) } }fn main() {let (pointer, length) get_memory_location();let message get_str_at_location(pointer, length);println!(The {} bytes at 0x{:X} stored: {},length, pointer, message);// 如果大家想知道为何处理裸指针需要 unsafe可以试着反注释以下代码// let message get_str_at_location(1000, 10); }1.2.1.4、使用 * 解引用 let a 1; let b: *const i32 a as *const i32; let c: *const i32 a; unsafe {println!({}, *c); } 使用 * 可以对裸指针进行解引用由于该指针的内存安全性并没有任何保证因此我们需要使用 unsafe 来包裹解引用的逻辑(切记unsafe 语句块的范围一定要尽可能的小具体原因在上一章节有讲)。 以上代码另一个值得注意的点就是除了使用 as 来显式的转换我们还使用了隐式的转换方式 let c: *const i32 a;。在实际使用中我们建议使用 as 来转换因为这种显式的方式更有助于提醒用户你在使用的指针是裸指针需要小心。 1.2.1.5、基于指针指针创建裸指针 let a: Boxi32 Box::new(10); // 需要先解引用a let b: *const i32 *a; // 使用 into_raw 来创建 let c: *const i32 Box::into_raw(a); 1.2.1.6、总结 使用裸指针可以让我们创建两个可变指针指向同一个数据如果使用安全的 Rust 是无法做到这一点违背了借用规则编译器会阻止。因此裸指针可以绕过借用规则由此带来的数据竞争问题需要大家自己处理。 重要用途就是跟 C 语言的代码进行交互(FFI)在讲解 FFI 之前先看看调用 unsafe 函数和方法。 1.2.2、调用不安全的函数或方法 1.2.2.1、调用不安全的函数或方法 unsafe 函数从外表上来看跟普通函数并无区别唯一区别就是需要使用 unsafe fn 来进行定义这种定义行为告诉调用者当调用此函数时候你需要注意它的相关需求因为 Rust 无法担保调用者在使用该函数时能满足它所需要的一切需求。 强制调用者加上 unsafe 语句块就可以让他清晰认识到正在调用一个不安全的函数需要小心看看文档看看函数有哪些特别的要求需要被满足。 unsafe fn dangerous() {} fn main() {dangerous(); }如果试图这样调用编译器就会报错 error[E0133]: call to unsafe function is unsafe and requires unsafe function or block-- src/main.rs:3:5| 3 | dangerous();| ^^^^^^^^^^^ call to unsafe function 范例修改 unsafe fn dangerous() {} fn main() {unsafe{dangerous();} }使用 unsafe 声明的函数时候一定要看看相关文档确定自己没有遗漏什么。 1.2.2.2、用安全抽象包裹 unsafe 代码 一个函数包含了 unsafe 不代表我们需要将整个函数定义 unsafe fn。事实上在标准库中有大量的安全函数他们内部都包含了 unsafe 函数。 对于 Rust 的借用检查器来说它无法理解我哦们分别借用了同一个切片的两个不同部分但是事实上这种行为是没有问题毕竟两个借用没有任何重叠之处。 use std::slice;fn split_at_mut(slice: mut [i32], mid: usize) - (mut [i32], mut [i32]) {let len slice.len();let ptr slice.as_mut_ptr();assert!(mid len);unsafe {(slice::from_raw_parts_mut(ptr, mid),slice::from_raw_parts_mut(ptr.add(mid), len - mid),)} }fn main() {let mut v vec![1, 2, 3, 4, 5, 6];let r mut v[..];let (a, b) split_at_mut(r, 3);assert_eq!(a, mut [1, 2, 3]);assert_eq!(b, mut [4, 5, 6]); }相比安全事项这段代码没有那么好理解我们甚至需要像C语言那样通过指针地址的偏移去控制数组的分割。 1.2.2.3、FFI(Foreign Function Interface) FFI(Foreign Function Interface) 可以用来与其他语言进行交互但是并不是所有语言都这么称呼例如 Java 称之为 JNI(Java Native Interface)。 FFI 之所以存在是由于现实中很多代码库都是由不同语言编写的如果我们需要使用某个库但是它是由其它语言编写的那么往往只有两个选择 对该库进行重写或移植使用 FFI 前者相当不错但是在很多时候并没有那么多时间去重写因此 FFI 就成了最佳选择。回到 Rust 语言上由于这门语言依然很年轻一些生态是缺失的我们在写一些不是那么大众的项目时可能会同时遇到没有相应的 Rust 库可用的尴尬境况此时通过 FFI 去调用 C 语言的库就成了相当棒的选择。 还有在将 C/C 的代码重构为 Rust 时先将相关代码引入到 Rust 项目中然后逐步重构也是不错的(为什么用不错来形容因为重构一个有一定规模的 C/C 项目远没有想象中美好因此最好的选择还是对于新项目使用 Rust 实现老项目。。就让它先运行着吧)。 当然除了 FFI 还有一个办法可以解决跨语言调用的问题那就是将其作为一个独立的服务然后使用网络调用的方式去访问HTTPgRPC 都可以。 言归正传之前我们提到 unsafe 的另一个重要目的就是对 FFI 提供支持它的全称是 Foreign Function Interface顾名思义通过 FFI , 我们的 Rust 代码可以跟其它语言的外部代码进行交互。 范例 extern C {fn abs(input: i32) - i32; }fn main() {unsafe {println!(Absolute value of -3 according to C: {}, abs(-3));} }C 语言的代码定义在了 extern 代码块中 而 extern 必须使用 unsafe 才能进行进行调用原因在于其它语言的代码并不会强制执行 Rust 的规则因此 Rust 无法对这些代码进行检查最终还是要靠开发者自己来保证代码的正确性和程序的安全性。 1.2.2.4、ABI(Application Binary Interface) ABI 定义了如何在汇编层面来调用该函数在所有的ABI中C语言是最常见的。 1.2.2.5、其他语言调用 Rust 函数 在 Rust 中调用其它语言的函数是让 Rust 利用其他语言的生态那反过来可以吗其他语言可以利用 Rust 的生态不答案是肯定的。 我们可以使用 extern 来创建一个接口其它语言可以通过该接口来调用相关的 Rust 函数。但是此处的语法与之前有所不同之前用的是语句块而这里是在函数定义时加上 extern 关键字当然别忘了指定相应的 ABI 范例 #[no_mangle] pub extern C fn call_from_c() {println!(Just called a Rust function from C!); }上述代码可以编译称一个共享库然后链接到C语言。 #[no_mangle] 注解告诉编译器不要乱改函数的名称。 Mangling 的定义是当 Rust 因为编译需要去修改函数的名称例如为了让名称包含更多的信息这样其它的编译部分就能从该名称获取相应的信息这种修改会导致函数名变得相当不可读。 因此为了让 Rust 函数能顺利被其它语言调用我们必须要禁止掉该功能 1.2.3、访问或修改可变静态变量 我们在全局变量章节中讲解过这里不讲述了 1.2.4、实现不安全的Trait unsafe 的trait 确实不多见如果大家还记得话我们在之前的 Send 和 Sync 章节过实现过 unsafe 特征 Send。 范例unsafe trait 声明很简单 unsafe trait Foo {// 方法列表 }unsafe impl Foo for i32 {// 实现相应的方法 }fn main() {}1.2.5、访问联合体的字段 union 是用于 C 代码进行交互。访问 union 的字段是不安全的因为 Rust 无法保证存在在 union 实例中的数据类型。 #[repr(C)] union MyUnion {f1: u32,f2: f32, }从上述代码可以看出union 的使用方式跟结构体确实很相似但是前者的所有字段都共享同一个存储空间意味着往 union 的某个字段写入值会导致其它字段的值会被覆盖。 关于 Union 可以查看 https://doc.rust-lang.org/reference/items/unions.html。 1.2.6、一些实用库 1、rust-bindgen 和 cbindgen 对于 FFI 的调用来说保证接口的正确性是非常重要的这两个库可以帮我们自动生成相应的接口其中 rust-bindgen 用于在 Rust 中访问 C 代码而 cbindgen则反之。 2、cxx 如果需要跟 C 代码交互非常推荐使用 cxx它提供了双向的调用最大的优点就是安全是的你无需通过 unsafe 来使用它 3、Miri miri 可以生成 Rust 的中间层表示 MIR对于编译器来说我们的 Rust 代码首先会被编译为 MIR 然后再提交给 LLVM 进行处理。 可以通过 rustup component add miri 来安装它并通过 cargo miri 来使用同时还可以使用 cargo miri test 来运行测试代码。 miri 可以帮助我们检查常见的未定义行为(UB Undefined Behavior)以下列出了一部分: 内存越界检查和内存释放后再使用(use-after-free)使用未初始化的数据数据竞争内存对齐问题 但是需要注意的是它只能帮助识别被执行代码路径的风险那些未被执行到的代码是没办法被识别的。 4、Clippy 官方的 clippy 检查器提供了有限的 unsafe 支持虽然不多但是至少有一定帮助。例如 missing_safety_docs 检查可以帮助我们检查哪些 unsafe 函数遗漏了文档。 需要注意的是 Rust 编译器并不会默认开启所有检查大家可以调用 rustc -W help 来看看最新的信息。 5、prusti prusti 需要大家自己来构建一个证明然后通过它证明代码中的不变量是正确被使用的当你在安全代码中使用不安全的不变量时就会非常有用。 6、模糊测试(fuzz testing) cargo install cargo-fuzz二、高级 Trait 2.1、在 Trait定义中使用关联类型来指定占位类型 关联类型(associated type) 是 Trait 中 的类型占位符它可以用于 Trait 的方法签名中可以定义包含某些类型的 Trait 而实现前无需要知道这些类型是什么 pub trait Iterator {type Item;fn next(mut self) - OptionSelf::Item; }2.2、关联类型与泛型区别 泛型关联类型每次实现 Trait 时标注类型无需标注类型可以为一个类型多次实现某个 trait不同的泛型参数无法为单个类型多次实现某个 Trait 2.3、默认泛型参数和运算符重载(operator overloading) 可以在使用泛型参数时为泛型指定一个默认的具体类型语法 PlaceholderTypeConcreteType 这种技术常用于运算符重载 Rust 不允许创建自己的运算符以及重载任意的运算符但是可以通过实现 std::ops 中列出的那些 Trait 来重载一部分相应的运算符。 use std::ops::Add;#[derive(Debug, Copy, Clone, PartialEq)] struct Point {x: i32,y: i32, }impl Add for Point {type Output Point;fn add(self, other: Point) - Point {Point {x: self.x other.x,y: self.y other.y,}} }fn main() {assert_eq!(Point { x: 1, y: 0 } Point { x: 2, y: 3 },Point { x: 3, y: 3 }); }Add Trait 声明如下 trait AddRhsSelf {type Output;fn add(self, rhs: Rhs) - Self::Output; } 2.4、默认泛型参数的主要场景 扩展一个类型不破坏现有代码允许在大部分用户都不需要的特定场景下进行自定义 2.5、完全限定语法(Fully Qualified Syntax) 示例代码 trait Pilot {fn fly(self); }trait Wizard {fn fly(self); }struct Human;impl Pilot for Human {fn fly(self) {println!(This is your captain speaking.);} }impl Wizard for Human {fn fly(self) {println!(Up!);} }impl Human {fn fly(self) {println!(*waving arms furiously*);} }fn main() {let person Human;person.fly(); }默认调用 Human 的fly方法我们如果要调用 Trait 的实现方法可以使用下列调用方式 fn main() {let person Human;Pilot::fly(person);Wizard::fly(person);person.fly(); }如果我们要调用的是关联函数。 trait Animal {fn baby_name() - String; }struct Dog;impl Dog {fn baby_name() - String {String::from(Spot)} }impl Animal for Dog {fn baby_name() - String {String::from(puppy)} }fn main() {println!(A baby dog is called a {}, Dog::baby_name()); }此时如果我们想要调用 Animal 的 baby_name 语法则需要使用 完全限定语法。 fn main() {println!(A baby dog is called a {}, Dog as Animal::baby_name()); }完全限定语法形式如下 Type as Trait::function(receiver_if_method, next_arg, ...);2.6、使用 supertrait 要求 trait 附带其他 trait 的功能 有需要在一个 trait 使用其他 trait。 use std::fmt;trait OutlinePrint: fmt::Display {fn outline_print(self) {let output self.to_string();let len output.len();println!({}, *.repeat(len 4));println!(*{}*, .repeat(len 2));println!(* {} *, output);println!(*{}*, .repeat(len 2));println!({}, *.repeat(len 4));} } 2.7、使用 newtype 模式在外部类型上实现外部 trait 孤儿规则只有当 trait 或 类型定义在本地包才能为该类型实现这个trait。 可以通过 newtype 模式来绕过这一规则利用 tuple struct 创建一个新的类型。 use std::fmt;struct Wrapper(VecString);impl fmt::Display for Wrapper {fn fmt(self, f: mut fmt::Formatter) - fmt::Result {write!(f, [{}], self.0.join(, ))} }fn main() {let w Wrapper(vec![String::from(hello), String::from(world)]);println!(w {}, w); }三、高级类型 3.1、newtype模式 用来静态的保证各种值之间不会混淆并表明值的单位为类型的某些细节提供抽象能力通过轻量级的封装来隐藏内部实现细节 3.2、使用类型别名创建类型同义词 使用type 关键字创建类型别名类型别名并不是独立的类型。主要用途是减少代码字符重复 type Kilometers i32;let x: i32 5;let y: Kilometers 5;println!(x y {}, x y); 3.3、Never type 有一个名为 ! 的特殊类型它没有任何值行话叫做空类型empty type它在不返回的函数中充当返回类型 不返回值的函数也被称做发散函数(diverging function) fn bar() - ! {// --snip-- }这种代码会报错。 loop循环 和 continue 的返回类型就是 ! 3.3、动态大小 和 Size Trait 3.3.1、动态大小 Rust 需要在编译时确定为一个特定的类型值分配多少空间。动态大小的类型(Dynamically Sized Types, DST)概念编写代码时使用只有在运行时才能确定大小的值。 Rust 使用动态大小类型的通用方式附带一些元数据来存储动态信息的大小使用动态类型大小总会把它的值放在某种指针后面。 3.3.2、Sized Trait 为了处理动态大小的类型 Rust 提供了一个 Sized Trait 来确定一个类型的大小在编译时是否已知 编译时可以计算出大小的类型会自动实现这一个 traitRust 还会为每个泛型函数隐式添加 Sized 约束 3.3.4、?Sized Trait 约束 使用 ?Sized 表示 泛型 可能是 Sized 也可能不是Sized参数必须是引用。 四、高级函数和闭包 4.1、函数指针function pointer 可以将函数传递给其他函数函数在传递过程中会被强制转换 fn 类型fn 类型就是函数指针。 fn add_one(x: i32) - i32 {x 1 }fn do_twice(f: fn(i32) - i32, arg: i32) - i32 {f(arg) f(arg) }fn main() {let answer do_twice(add_one, 5);println!(The answer is: {}, answer); }函数指针是一个类型不是一个 trait全部实现了三种闭包 trait。 4.2、返回闭包 fn returns_closure() - Boxdyn Fn(i32) - i32 {Box::new(|x| x 1) }五、宏macro 5.1、宏 宏在 Rust 里面指的是一组相关特性的集合称谓 使用 macro_rules! 构建声明宏(declarative macro)三种过程宏 自定义 #[derive] 宏用于 struct 或 enum可以为其制定随 derive 属性添加的代码类似属性的宏(Attribute-like macro)在任意条目上添加自定义属性类似函数的宏(Function-like macro)看起来像函数调用对其指定为参数的 token 进行操作 5.2、宏和函数的区别 宏是通过一种代码生成另一种代码Rust 的函数签名是固定的而宏可以拥有可变数量的参数。编译器会在解释代码进行展开宏函数可以在任何位置定义和使用 5.3、macro_rules! 声明宏 #[macro_export] macro_rules! vec {( $( $x:expr ),* ) {{let mut temp_vec Vec::new();$(temp_vec.push($x);)*temp_vec}}; } #[macro_export] 注释将宏进行了导出这样其他包可以将宏导入到当前座女与中然后才能使用。标准库 vec宏已经通过 std::prelude 自动引入。 对于 macro_rules! 存在一些问题Rust 计划在未来使用新的生命宏来替换它。 5.4、用过程宏(procedural macros)为属性标记生成代码 从形式上来看过程宏跟函数较为相像但过程宏是使用源代码作为输入参数基于代码进行一系列操作后再输出一段全新的代码。注意过程宏中的 derive 宏输出的代码并不会替换之前的代码这一点与声明宏有很大的不同 有三种类型自定义 derive、属性宏、函数宏。 5.4.1、自定义 derive 宏 extern crate proc_macro;use proc_macro::TokenStream; use quote::quote; use syn; use syn::DeriveInput;#[proc_macro_derive(HelloMacro)] pub fn hello_macro_derive(input: TokenStream) - TokenStream {// 基于 input 构建 AST 语法树let ast:DeriveInput syn::parse(input).unwrap();// 构建特征实现代码impl_hello_macro(ast) }fn impl_hello_macro(ast: syn::DeriveInput) - TokenStream {let name ast.ident;let gen quote! {impl HelloMacro for #name {fn hello_macro() {println!(Hello, Macro! My name is {}!, stringify!(#name));}}};gen.into() }5.4.2、属性宏 #[proc_macro_attribute] pub fn route(attr: TokenStream, item: TokenStream) - TokenStream {5.4.3、函数宏 #[proc_macro] pub fn sql(input: TokenStream) - TokenStream {总结 以上就是今天要讲的内容
http://www.tj-hxxt.cn/news/222435.html

相关文章:

  • 建筑工程招投标网站自己有服务器怎么建设网站
  • 怎么夸一个网站开发公司西宁网站建设建站
  • 秦皇岛网站开发公司电子商务网站建设与管理目录
  • 上海人才网站建设兰州seo快速优化报价
  • 公司网站在百度搜不到推广渠道有哪些平台
  • 艺术设计专业灵感推荐网站网站推广策划包含的内容
  • 鞍山网站设计公司工商注册系统
  • 做钓鱼网站会被抓判刑吗东莞什么行业做网站的多
  • 网站建设项目的工作分解wordpress mysql 5.7
  • 云抢购网官方网站网站代理合作
  • 漂亮的网站单页网上广告设计培训
  • 事业单位 网站备案平板微信hd版
  • 石家庄网站建设公司怎么样网站开发提成
  • 低价格制作网站手机小游戏网站
  • wamp做的网站外网怎么访问wordpress玻璃透主题
  • 专业积分商城网站建设外贸网站建设服务机构
  • 网加做网站推广重庆建设工程信息网三类人员
  • 专业网站开发制作公司做应用级网站用什么语言好
  • 信阳建设企业网站山西省城乡建设厅网站
  • 手机怎么做网站卖东西模板网站修改
  • 1高端网站建设直缝钢管网站建设
  • 南昌高端网站开发费用表南京移动网站建设报价
  • 专业苏州网站建设公司汽车品牌大全汽车网
  • wordpress会员介绍页株洲seo优化官网
  • 移动互联网平台有哪些西安seo培训
  • 上海装修网站建设摄影网站的意义
  • 网站设计目前和将来的就业前景ui网页设计方法
  • 做国外的众筹网站有哪些wordpress怎么都是英文
  • 在网站做博客美仑美家具的网站谁做的
  • 门户网站是用户上网的第一入口引流推广app