[{"content":"关联类型 关联类型时在特征定义的语句块中，申明一个自定义类型，这样就可以在特征的方法签名中使用该类型。\n1 2 3 4 5 pub trait Iterator { type Item; fn next(\u0026amp;mut self) -\u0026gt; Option\u0026lt;Self::Item\u0026gt;; } Self用来指代当前调用者的具体类型，那么Self::Item就用来指代该类型实现中定义的Item类型：\n1 2 3 4 5 6 7 8 9 10 11 12 impl Iterator for Counter { type Item = u32; fn next(\u0026amp;mut self) -\u0026gt; Option\u0026lt;Self::Item\u0026gt; { //... } } fn main() { let c = Counter{..} c.next() } 上述代码中，Counter类型实现了Iterator特征，变量c是特征Iterator的实例，也是next方法的调用者。\n为什么不使用泛型:\n1 2 3 pub trait Iterator\u0026lt;Item\u0026gt;{ fn next(\u0026amp;mut self) -\u0026gt; Option\u0026lt;Item\u0026gt;; } 因为代码的可读性\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 // 泛型写法 trait Container\u0026lt;A,B\u0026gt; { fn contains(\u0026amp;self,a: A,b: B) -\u0026gt;bool; } fn difference\u0026lt;A,B,C(container:\u0026amp;C) -\u0026gt; i32 where c: Container\u0026lt;A,B\u0026gt; {...} // 关联类型实现 trait Container{ type A; type B; fn copntains(\u0026amp;self, a:Self::A,b:\u0026amp;Self::B) -\u0026gt; bool; } fn differnce\u0026lt;C: Container\u0026gt;(container:\u0026amp;:C) {} 默认泛型类型参数 在外部类型上实现外部特征（newtype） 1 2 3 4 5 6 7 8 9 10 11 12 13 14 use std::fmt; struct Wrapper(Vec\u0026lt;String\u0026gt;); impl fmt::Display for Wrapper { fn fmt(\u0026amp;self, f:\u0026amp;mut fmt::Formatter) -\u0026gt; fmt::Result { write!(f, \u0026#34;[{}]\u0026#34;, self.0.join(\u0026#34;, \u0026#34;)) } } fn main() { let w = Wrapper(vec![String::from(\u0026#34;hello\u0026#34;), String::from(\u0026#34;world\u0026#34;)]); println!(\u0026#34;w = {}\u0026#34;, w); } ","date":"2024-04-01T22:33:18+08:00","permalink":"https://mysetsuna.github.io/p/rust%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF%E6%B7%B1%E5%85%A5%E4%BA%86%E8%A7%A3%E7%89%B9%E5%BE%81/","title":"Rust学习之路——深入了解特征"},{"content":"在上一节中有一段代码无法通过边缘：\n1 2 3 4 5 6 7 8 9 10 11 fn returns_summarizable(switch: bool) -\u0026gt; impl Summary { if switch { Post { // ... } } else { Weibo { // ... } } } 其中Post和Weibo都实现了Summary 特征，因此上面的函数试图通过返回 impl Summary 来返回这两个类型，但是编译器却无情地报错了，原因是 impl Trait 的返回值类型并不支持多种不同的类型返回，那如果我们想返回多种类型，该怎么办？\n再来考虑一个问题：现在在做一款游戏，需要将多个对象渲染在屏幕上，这些对象属于不同的类型，存储在列表中，渲染的时候，需要循环该列表并顺序渲染每个对象，在 Rust 中该怎么实现？\n使用枚举\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 #[derive(Debug)] enum UiObject { Button, SelectBox, } fn main() { let object = [ UiObject::Button, UiObject::SelectBox ]; for o in object { draw(o) } } fn draw(o: UiObject) { println!(\u0026#34;{:?}\u0026#34;, o); } 问题，如果对象集合并不能事先明确的知道？缺失或想要新增枚举类型？\n我们无法知道所有的UI对象类型，只是知道：\nUI对象的类型不同 需要一个统一的类型来处理这些对象，无论是作为函数参数还是作为列表中的一员 需要对每个对象调用draw方法 在拥有继承的语言中，可定义一个名为Component的类，该类上有一个draw方法。其他的类比如Button、Image和SelectBox会从Component派生并因此继承draw方法。它们各自都可以覆盖draw方法来定义自己的行为，但是框架会把所有这些类型当作是Component的实例，并在其上调用draw。不过Rust并没有继承。\n特征对象定义 为了解决上面的所有问题，Rust引入了一个概念\u0026ndash;特征对象。 先定义一个特征\n1 2 3 pub trait Draw { fn draw(\u0026amp;self); } 只要实现了Draw特征，就可以调用draw方法来进行渲染。假设有个Button和SelectBox组件实现了Draw特征：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 pub struct Button { pub width: u32, pub height: u32, pub label: String, } impl Draw for Button { fn draw(\u0026amp;self) { // 绘制按钮代码 } } struct SelectBox { width: u32, height: u32, options: Vec\u0026lt;String\u0026gt;, } impl Draw for SelectBox { fn draw(\u0026amp;self) { // 绘制SelectBox的代码 } } //需要一个动态数组来存储这些UI对象 pub struct Screen { pub components: Vec\u0026lt;?\u0026gt; } 代码中的?: 可以替换为Draw的特征对象，通过\u0026amp;引用或者Box\u0026lt;T\u0026gt;智能指针的方式来创建特征对象。\ntips: Box\u0026lt;T\u0026gt;包裹的值会被强制分配在堆上\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 trait Draw { fn draw(\u0026amp;self)-\u0026gt;String; } impl Draw for u8 { fn draw(\u0026amp;self) -\u0026gt; String { format!(\u0026#34;u8: {}\u0026#34;, *self) } } impl Draw for f64 { fn draw(\u0026amp;self) -\u0026gt; String { format!(\u0026#34;f64: {}\u0026#34;, *self) } } // 若 T 实现了Draw特征，则调用该函数时传入的Box\u0026lt;T\u0026gt;可以被隐式转换成函数参数签名中的Box\u0026lt;dyn Draw\u0026gt; fn draw1(x: Box\u0026lt;dyn Draw\u0026gt;) { // 由于实现了 Deref特征，Box智能指针会自动解引用为它所包裹的值，然后调用该值对应的类型上定义的`draw`方法 x.draw(); } fn draw2(x: \u0026amp;dyn Draw) { x.draw(); } fn main { let x = 1.1f64; let y = 8u8; // x和y的类型T都实现了`Draw`特征，因为Box\u0026lt;T\u0026gt;可以在函数调用时隐式地被转换为特征对象 Box\u0026lt;dyn Draw\u0026gt; // 基于x的值创建一个Box\u0026lt;f64\u0026gt;类型的智能指针，指针指向的数据被放置在了堆上 draw1(Box::new(x)); // 基于 y 的值创建一个 Box\u0026lt;u8\u0026gt; 类型的智能指针 draw1(Box::new(y)); draw2(\u0026amp;x); draw2(\u0026amp;y); } draw1 函数的参数是 Box\u0026lt;dyn Draw\u0026gt; 形式的特征对象，该特征对象是通过 Box::new(x) 的方式创建的 draw2 函数的参数是 \u0026amp;dyn Draw形式的特征对象，该特征对象是通过 \u0026amp;x 的方式创建的 dyn 关键字只用在特征对象的类型声明上，在创建时无需使用 dyn 因此，可以使用特征对象来代表泛型或具体的类型。\n1 2 3 pub struct Screen { pub components: Vec\u0026lt;Box\u0026lt;dyn Draw\u0026gt;\u0026gt;, } 其中存储了一个动态数组，里面元素的类型是 Draw 特征对象：Box\u0026lt;dyn Draw\u0026gt;，任何实现了 Draw 特征的类型，都可以存放其中。\n再来为 Screen 定义 run 方法，用于将列表中的 UI 组件渲染在屏幕上：\n1 2 3 4 5 6 7 impl Screen { pub fn run(\u0026amp;self) { for component in self.components.iter() { component.draw(); } } } 在列表中存储多种不同类型的实例，然后将它们使用同一个方法逐一渲染在屏幕上！\n泛型实现:\n1 2 3 4 5 6 7 8 9 10 11 12 pub struct Screen\u0026lt;T: Draw\u0026gt; { pub components: Vec\u0026lt;T\u0026gt;, } impl\u0026lt;T\u0026gt; Screen\u0026lt;T\u0026gt; where T: Draw { pub fn run(\u0026amp;self) { for component in self.components.iter() { component.draw(); } } } 上面的Screen的列表中，存储了类型为T的元素，然后在Screen中使用特征约束让T实现了Draw特征，进而可以调用draw方法。\n这种写法限制了Screen实例的Vec\u0026lt;T\u0026gt;中每个元素必须时Button类型或者全是SelectBox类型。如果只需要相同类型集合，更倾向于采用泛型+特征约束这种写法，因为这样实现更清晰，性能更好。（特征对象需要在运行时从vtable动态查找需要调用的方法）\n运行\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 fn main() { let screen =Screen { components: vec![ Box::new(SelectBox{ width:75, height:10, options:vec![ String::from(\u0026#34;Yes\u0026#34;), String::from(\u0026#34;Maybe\u0026#34;), String::from(\u0026#34;No\u0026#34;) ], }), Box::new(Button { width: 50, height: 10, label: String::from(\u0026#34;OK\u0026#34;,) }), ], }; screen.run(); } 上面使用 Box::new(T) 的方式来创建了两个 Box\u0026lt;dyn Draw\u0026gt; 特征对象，如果以后还需要增加一个 UI 组件，那么让该组件实现 Draw 特征，则可以很轻松的将其渲染在屏幕上，甚至用户可以引入我们的库作为三方库，然后在自己的库中为自己的类型实现 Draw 特征，然后进行渲染。\n在动态类型语言中，有一个很重要的概念： 鸭子类型（duck typing），简单来说，就是只关心值长啥样，而不关心它实际是什么。当一个东西走起来像鸭子，叫起来像鸭子，那么它就是一只鸭子，就算它实际上是一个奥特曼，也不重要，我们就当它是鸭子。\n使用特征对象和Rust类型系统来进行类似鸭子类型操作的优势是，无需在运行时检查一个值是否实现了特定方法或者担心在调用时因为值没有实现方法而产生错误。如果值没有实现特征对象所需的特征，那么Rust根本就不会编译这些代码。\n1 2 3 4 5 6 7 fn main() { let screen = Screen { components: vec!{ Box::new(String::from(\u0026#34;Hi\u0026#34;)), }, }; } 因为String类型没有实现Draw特征，编译器直接就会报错，不会让上述代码运行。如果想要String类型被渲染在屏幕上，只需要为其实现Draw特征即可。\n注意dyn不能单独作为特征对象的定义。特征对象可以是任意实现了每个特征的类型，编译器在编译期间不知道该类型的大小。不同类型的大小是不同的。\n\u0026amp;dyn 和 Box 在编译期都是已知大小。\n1 2 3 4 5 6 7 8 9 fn draw2(x: dyn Draw) { x.draw(); } 10 | fn draw2(x: dyn Draw) { | ^ doesn\u0026#39;t have a size known at compile-time | = help: the trait `Sized` is not implemented for `(dyn Draw + \u0026#39;static)` help: function arguments must have a statically known size, borrowed types always have a known size 特征对象的动态分发 泛型是在编译期完成处理的：编译器会为每一个泛型参数对应的具体类型生成一份代码，这种方式是静态分发(static dispatch)，因为是在编译期完成的，对于运行期性能完全没有任何影响。\n与静态分发相对应的是动态分发(dynamic dispatch)，在这种情况下，直到运行时，才能确定需要调用什么方法。之前代码中的关键字 dyn 正是在强调这一“动态”的特点。\n当使用特征对象时，Rust 必须使用动态分发。编译器无法知晓所有可能用于特征对象代码的类型，所以它也不知道应该调用哪个类型的哪个方法实现。为此，Rust 在运行时使用特征对象中的指针来知晓需要调用哪个方法。动态分发也阻止编译器有选择的内联方法代码，这会相应的禁用一些优化。\n下面这张图很好的解释了静态分发 Box 和动态分发 Box 的区别：\n特征对象大小不固定：这是因为，对于特征 Draw，类型 Button 可以实现特征 Draw，类型 SelectBox 也可以实现特征 Draw，因此特征没有固定大小 几乎总是使用特征对象的引用方式，如 \u0026amp;dyn Draw、Box\u0026lt;dyn Draw\u0026gt; 虽然特征对象没有固定大小，但它的引用类型的大小是固定的，它由两个指针组成（ptr 和 vptr），因此占用两个指针大小 一个指针 ptr 指向实现了特征 Draw 的具体类型的实例，也就是当作特征 Draw 来用的类型的实例，比如类型 Button 的实例、类型 SelectBox 的实例 另一个指针 vptr 指向一个虚表 vtable，vtable 中保存了类型 Button 或类型 SelectBox 的实例对于可以调用的实现于特征 Draw 的方法。当调用方法时，直接从 vtable 中找到方法并调用。之所以要使用一个 vtable 来保存各实例的方法，是因为实现了特征 Draw 的类型有多种，这些类型拥有的方法各不相同，当将这些类型的实例都当作特征 Draw 来使用时(此时，它们全都看作是特征 Draw 类型的实例)，有必要区分这些实例各自有哪些方法可调用 简而言之，当类型 Button 实现了特征 Draw 时，类型 Button 的实例对象 btn 可以当作特征 Draw 的特征对象类型来使用，btn 中保存了作为特征对象的数据指针（指向类型 Button 的实例数据）和行为指针（指向 vtable）。\n一定要注意，此时的 btn 是 Draw 的特征对象的实例，而不再是具体类型 Button 的实例，而且 btn 的 vtable 只包含了实现自特征 Draw 的那些方法（比如 draw），因此 btn 只能调用实现于特征 Draw 的 draw 方法，而不能调用类型 Button 本身实现的方法和类型 Button 实现于其他特征的方法。也就是说，btn 是哪个特征对象的实例，它的 vtable 中就包含了该特征的方法。\nSelf 与 self 在Rust中,有两个self，一个指代当前的实例对象，一个指代特征或方法类型的别名。\n1 2 3 4 5 6 7 8 9 10 11 trait Draw { fn draw(\u0026amp;self) -\u0026gt; Self; } #[derive(Clone)] struct Button; impl Draw for Button { fn draw(\u0026amp;self) -\u0026gt; Self { return self.clone() } } ","date":"2024-03-25T22:42:09+08:00","permalink":"https://mysetsuna.github.io/p/rust%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF%E7%89%B9%E5%BE%81%E5%AF%B9%E8%B1%A1/","title":"Rust学习之路——特征对象"},{"content":"定义 数据结构是数据对象，以及存在于该对象的实例和组成实例的数据元素之间的各种联系。这些联系可以通过定义相关的函数来给出 。 \u0026mdash;\u0026ndash;Sartaj Sahni, 《数据结构、算法与应用》\n数据结构是ADT(抽象数据类型Abstract Data Type)的物理实现 \u0026mdash;\u0026ndash;Cliffor A.Shaffer, 《数据结构与算法分析》\n数据结构（data structure）是计算机中存储、组织数据的方式。通常情况下，精心选择的数据结构可以带来最优效率的算法。 \u0026mdash;中文维基百科\n例一：书架如何放书 方法一 随便发 哪里有空放哪 找书是个噩梦 方法二 按照书名的拼音字母顺序排放 操作一：新书怎么插入\n如果书位置靠前，所有的书都要腾位置 操作二：怎么找到指定的某本书\n二分查找 方法三 把书架划分成几块区域，每块区域指定拜访某种类别的图书；在每种类别内，按照书名的拼英字母顺序排放 操作一：新书怎么插入\n先定类别，二分查找确定位置，移出空位 操作二：怎么找到指定的某本书\n先定类别，再二分查找 问题：每种书类的规模，类别大小\n例一总结 解决问题方法的效率，跟数据的组织方式有关。\n例二： 写程序实现一个函数PrintN，是的传入一个正整数为N的参数后，能顺序打印从1到N的全部正整数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 // 循环实现 const PrintN = (N:number) =\u0026gt; { for(let i = 1; i\u0026lt;=N; i++){ console.log(i); } } //10万 正常打印 // 递归 const PrintN1 = (N: number) =\u0026gt; { if(N) { PrintN1(N - 1); console.log(N); } } //10万 内存溢出 例二总结 解决问题方法的效率，跟空间的利用效率有关。\n例三：写程序计算给定多项式在给定点X处的值 题目： $$ f(x) = a_0 +a_1x +\u0026hellip;+ a_{n -1 }x^{n-1}+a_nx^n $$\n1 2 3 4 5 6 7 const f = (n: number, a:number[], x:number) =\u0026gt; { let p = a[0]; for(let i = 1;i\u0026lt;=n;i++){ p+=(a[i] * Math.pow(x,i)); } return p; } 转化为结合律 $$ f(x)=a_0+x(a_1 + x(\u0026hellip;(a_{n-1}+x(a_n))\u0026hellip;)) $$\n1 2 3 4 5 6 7 const f = (n: number, a:number[], x:number) =\u0026gt; { let p = a[n]; for(i = n;i\u0026gt;0li--){ p = a[i-1] + x*p; } return p; } 例三总结 解决问题方法的效率，跟算法的巧妙程度有关。\n到底什么是数据结构 数据对象在计算机中的组织方式\n逻辑结构 物理存储结构 数据对象必定与一系列加在其上的操作相关联\n完成这些操作所用的方法就是算法\n抽象数据类型（Abstract Data Type） 数据类型 数据对象集 数据集合相关联的操作集 抽象： 描述数据类型的方法不依赖于具体实现 与存放数据的机器无关 与数据存储的物理结构无关 与实现操作的算法和编程语言无关 只描述数据对象集合相关操作集是什么，并不涉及如何做到的问题\n例四 矩阵的抽象数据类型定义 类型名称：矩阵（Matrix）\n数据对象集：一个M×N的矩阵 $A_{m×n}=(a_{ij})(i=1,\u0026hellip;,M;j=1,\u0026hellip;,N)$ 由M×N个三元组\u0026lt;a,i,j\u0026gt;构成，其中a是矩阵元素的值，i是元素所在的行号，j是元素所在的列号\n操作集：对于任意矩阵A, B, C c Matrix, 以及整数i、j、M、N\nMatrix Create (int M, int N ):返回一个M×N的空矩阵； int GetMaxRow(Matrix A): 返回矩阵A的总行数； int GetMaxCol(Matrix A): 返回矩阵A的总列数； ElementType GetEntry(Matrix A, int i, init j): 返回矩阵A的第i行、第j列的元素； Matrix Add(Matrix A, Matrix B): 如果A和B的行、列数一致，则返回矩阵C=A+B, 否则返回错误标志； Matrix Multiply(Matrix A, Matrix B): 如果A的列数等于B的行数，则返回矩阵C=AB，否则返回错误标志； ","date":"2024-03-21T23:07:31+08:00","permalink":"https://mysetsuna.github.io/p/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%E7%AC%AC%E4%B8%80%E8%AE%B2%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E4%B8%80%E4%BB%80%E4%B9%88%E6%98%AF%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/","title":"数据结构第一讲：基本概念（一）——什么是数据结构"},{"content":"通过Whistle.js配合浏览器插件SwitchyOmega，可以灵活的代理url。\neg：本地3000接口代理mysite.com域名下，除了*/db之外的所有请求，\n1 2 3 4 5 6 7 8 127.0.0.1:3000 mysite.com excludeFilter://*/db mysite.com reqHeaders://{header} // values header = { \u0026#34;someHeader\u0026#34;: \u0026#34;myParams\u0026#34; } ","date":"2024-03-12T15:50:26+08:00","permalink":"https://mysetsuna.github.io/p/whistle%E4%BD%BF%E7%94%A8%E6%8A%80%E5%B7%A7/","title":"Whistle使用技巧"},{"content":"—— 本文内容从《Rust语言圣经（Rust Course）》中归纳\n特征 Trait 1 2 3 4 fn add\u0026lt;T: std::ops::Add\u0026lt;Output = T\u0026gt;\u0026gt;(a:T, b:T) -\u0026gt; T { a + b } Trait 定义了一组可以被共享的行为，只要实现了特征，你就能实现这组行为。\n定义 Trait 定义特征是把一些方法组合在一起，目的是定义一个实现某些目标所必需的行为的合集。\n1 2 3 4 pub trait Summary { fn summarize(\u0026amp;self) -\u0026gt; String; } 这里使用trait关键字来声明一个特征，Summary是特证名。在大括号中定义了该特征的所有方法。\n特征只定义行为看起来是什么样的，而不定义行为具体是什么样的。\n实现这个特征的类型都需要具体实现该特征的相应方法。\n为类型实现特征 特征只定义行为，需要为类型实现具体的特征，定义具体行为。\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 pub trait Summary { fn summarize(\u0026amp;self) -\u0026gt; String; } pub struct Post { pub title: String, // 标题 pub author: String, // 作者 pub content: String, // 内容 } impl Summary for Post { fn summarize(\u0026amp;self) -\u0026gt; String { format!(\u0026#34;文章{}, 作者是{}\u0026#34;, self.title, self.author) } } pub struct Weibo { pub username: String, pub content: String } impl Summary for Weibo { fn summarize(\u0026amp;self) -\u0026gt; String { format!(\u0026#34;{}发表了微博{}\u0026#34;, self.username, self.content) } } fn main() { let post = Post{title: \u0026#34;Rust语言简介\u0026#34;.to_string(),author: \u0026#34;Sunface\u0026#34;.to_string(), content: \u0026#34;Rust棒极了!\u0026#34;.to_string()}; let weibo = Weibo{username: \u0026#34;sunface\u0026#34;.to_string(),content: \u0026#34;好像微博没Tweet好用\u0026#34;.to_string()}; println!(\u0026#34;{}\u0026#34;,post.summarize()); println!(\u0026#34;{}\u0026#34;,weibo.summarize()); } 特征定义与实现的位置（孤儿规则） ！！重要原则（孤儿原则） 若为类型A实现特征T，则A或T至少有一个需要时在当前作用域中定义的\n这可以确保他人编写的代码不会破坏你的代码\n默认实现 默认实现不要求重载Trait内的方法\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 pub trait Summary { fn summarize(\u0026amp;self) -\u0026gt; String { String::from(\u0026#34;(Read more...)\u0026#34;) } } impl Summary for Post {} impl Summary for Weibo { fn summarize(\u0026amp;self) -\u0026gt; String { format!(\u0026#34;{}发表了微博{}\u0026#34;, self.username, self.content) } } // Summary for Post: (Read more...) // Summary for Weibo :sunface发表了微博好像微博没Tweet好用 默认实现可以调用相同特征中的其他方法，哪怕这些方法没有默认实现\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 // 定义 pub trait Summary { fn summarize_author(\u0026amp;self) -\u0026gt; String; fn summarize(\u0026amp;self) -\u0026gt; String { format!(\u0026#34;(Read more from {}...)\u0026#34;, self.summarize_author()) } } //调用 impl Summary for Weibo { fn summarize_author(\u0026amp;self) -\u0026gt; String { format!(\u0026#34;@{}\u0026#34;, self.username) } } println!(\u0026#34;1 new weibo: {}\u0026#34;, weibo.summarize()); 使用特征作为函数参数 特征作为函数参数\n1 2 3 pb fun notify(item: \u0026amp;impl Summary) { println!(\u0026#34;Breaking news! {}\u0026#34;, item.summarize()); } impl Summary,顾名思义，指实现了Summary特征的item参数。\n特征约束（trait bound） impl [Trait Name] 实际上是一个语法糖，完整形式是：\n1 2 3 pub fn notify\u0026lt;T: Summary\u0026gt;(item: \u0026amp;T){ println!(\u0026#34;Breaking news!\u0026#34;, item.summarize()); } 完整的书写形式：T: [Trait Name]，被称为特征约束。\nimpl [Trait Name] 适用于简单场景，\n1 pub fn notify(item1: \u0026amp;impl Summary, item2: \u0026amp;impl Summary) {} T: [Trait Name]可以进一步约束参数必须类型相同\n1 pub fn notify\u0026lt;T:Summary\u0026gt;(item: \u0026amp;T, item2: \u0026amp;T){} 多重约束 1 2 3 4 5 // 语法糖 pub fn notify(item: \u0026amp;(impl Summary + Display)) {} // 特征约束 pub fn notify\u0026lt;T: Summary + Display\u0026gt;(item: \u0026amp;T) {} Where 约束 1 2 3 4 5 6 7 8 // 特征约束变多，签名变得复杂 fn some_function\u0026lt;T: Display + Clone, U: Clone + Debug\u0026gt;(t: \u0026amp;T, u: \u0026amp;U) -\u0026gt; i32 {} // 通过where 简化 fn some_fucntion\u0026lt;T, U\u0026gt;(t: \u0026amp;T, u: \u0026amp;U) -\u0026gt; i32 where T: Display + Clone, U: Clone + Debug {} 使用特征约束有条件地实现方法或特征 指定类型 + 指定特征的条件下去实现方法\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 use std::fmt::Display; struct Pair\u0026lt;T\u0026gt;{ x:T, y:T, } impl\u0026lt;T\u0026gt; Pair\u0026lt;T\u0026gt;{ fn new(x:T, y:Y) -\u0026gt; Self { Self { x, y, } } } // 只有T同时实现了Display + PartialOrd得Pair\u0026lt;T\u0026gt;才有cmp_display方法 impl\u0026lt;T: Display + PartialOrd\u0026gt; Pair\u0026lt;T\u0026gt; { fn cmp_display(\u0026amp;self) { if self.x \u0026gt;= self.y { println!(\u0026#34;The largest member is x = {}\u0026#34;, self.x); }else{ println!(\u0026#34;The largest member is y = {}\u0026#34;, self.y); } } } 有条件地实现特征 标准库为任何实现Display特征的类型实现了ToString特征：\n1 2 3 4 5 6 7 impl\u0026lt;T: Display\u0026gt; ToString for T { //... } //任何实现了Display的特征的类型调用由ToString定义的to_string方法。 let s = 3.to_string(); 函数中返回 impl Trait 可以通过impl Trait来说明一个函数返回了实习了指定Trait的类型\n1 2 3 4 5 6 7 8 fn returns_summarizable() -\u0026gt; impl Summary { Weibo { username: String::from(\u0026#34;sunface\u0026#34;), content: String::from( \u0026#34;m1 max太厉害了，电脑再也不会卡\u0026#34;, ) } } 注意，这里只能让函数调用者知道，函数的返回值实现了Summary这个Trait，却没有告知调用者，返回值的具体类型。 这种写法在返回类型特别复杂，无法明确定义的情况下特别有用，但是要注意的是： 返回值只能是一种具体类型，例如\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 fn returns_summarizable(switch: bool) -\u0026gt; impl Summary { if switch { Post { title: String::from ( \u0026#34;Penguins win the Stanley Cup Championship!\u0026#34;, ), author: String::from(\u0026#34;Iceburgh\u0026#34;), content: String::from( \u0026#34;The Pittsburgh Penguins once again are then best hockey team in the NHL.\u0026#34;, ) } } else { Weibo { username: String::from(\u0026#34;horse_ebooks\u0026#34;), content: String::from( \u0026#34;of course,as you probaly alread knoew,people\u0026#34;, ) } } } 以上代码无法通过编译，因为返回了不同的类型。如果想实现返回不同的类型，需要使用下一章节中的特征对象\n修复上一节中largest函数 上一节例子中编译报错问题 // todo\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 fn largest\u0026lt;T\u0026gt;(list: \u0026amp;[T]) -\u0026gt; T { let mut largest = list[0]; fro \u0026amp;item in list.iter() { if item \u0026gt; largest { largest = item; } } largest } fn main() { let number =_list =vec![34, 58, ] } error[E0369]: binary operation `\u0026gt;` cannot be applied to type `T` // 无法在 `T` 类型上应用`\u0026gt;`运算符 --\u0026gt; src/main.rs:5:17 | 5 | if item \u0026gt; largest { | ---- ^ ------- T | | | T | help: consider restricting type parameter `T` // 考虑使用以下的特征来约束 `T` | 1 | fn largest\u0026lt;T: std::cmp::PartialOrd\u0026gt;(list: \u0026amp;[T]) -\u0026gt; T { | ^^^^^^^^^^^^^^^^^^^^^^ 在 largest 函数体中我们想要使用大于运算符（\u0026gt;）比较两个 T 类型的值。这个运算符是标准库中特征 std::cmp::PartialOrd 的一个默认方法。\n1 fn largest\u0026lt;T: PartialOrd\u0026gt;(list: \u0026amp;[T]) -\u0026gt; T {} 会出现新的错误：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 error[E0508]: cannot move out of type `[T]`, a non-copy slice --\u0026gt; src/main.rs:2:23 | 2 | let mut largest = list[0]; | ^^^^^^^ | | | cannot move out of here | help: consider using a reference instead: `\u0026amp;list[0]` error[E0507]: cannot move out of borrowed content --\u0026gt; src/main.rs:4:9 | 4 | for \u0026amp;item in list.iter() { | ^---- | || | |hint: to prevent move, use `ref item` or `ref mut item` | cannot move out of borrowed content 错误的核心是 cannot move out of type [T], a non-copy slice，原因是 T 没有实现 Copy 特性，因此我们只能把所有权进行转移，毕竟只有 i32 等基础类型才实现了 Copy 特性，可以存储在栈上，而 T 可以指代任何类型（严格来说是实现了 PartialOrd 特征的所有类型）。\n因此，为了让 T 拥有 Copy 特性，我们可以增加特征约束：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 fn largest\u0026lt;T: PartialOrd + Copy\u0026gt;(list: \u0026amp;[T]) -\u0026gt; T { let mut largest = list[0]; for \u0026amp;item in list.iter() { if item \u0026gt; largest { largest = item; } } largest } fn main() { let number_list = vec![34, 50, 25, 100, 65]; let result = largest(\u0026amp;number_list); println!(\u0026#34;The largest number is {}\u0026#34;, result); let char_list = vec![\u0026#39;y\u0026#39;, \u0026#39;m\u0026#39;, \u0026#39;a\u0026#39;, \u0026#39;q\u0026#39;]; let result = largest(\u0026amp;char_list); println!(\u0026#34;The largest char is {}\u0026#34;, result); } 通过derive派生特征 在本书中，形如#[derive(Debug)]的代码，这种是一种特征派生语法，被 derive 标记的对象会自动实现对应的默认特征代码，继承相应的功能。\nDebug 特征，它有一套自动实现的默认代码，当你给一个结构体标记后，就可以使用 println!(\u0026quot;{:?}\u0026quot;, s) 的形式打印该结构体的对象。\nCopy 特征，它也有一套自动实现的默认代码，当标记到一个类型上时，可以让这个类型自动实现 Copy 特征，进而可以调用 copy 方法，进行自我复制。\nderive 派生出来的是 Rust 默认给我们提供的特征，在开发过程中极大的简化了自己手动实现相应特征的需求。\n用于开发者输出的 Debug Debug 特征可以让指定对象输出调试格式的字符串，通过在 {} 占位符中增加 :? 表明，例如println!(\u0026ldquo;show you some debug info: {:?}\u0026rdquo;, MyObject);.\nDebug 特征允许以调试为目的来打印一个类型的实例，所以程序员可以在执行过程中看到该实例的具体信息。\n例如，在使用 assert_eq! 宏时， Debug 特征是必须的。如果断言失败，这个宏就把给定实例的值打印出来，这样程序员就能看到两个实例为什么不相等。\n等值比较的 PartialEq 和 Eq PartialEq 特征可以比较一个类型的实例以检查是否相等，并开启了 == 和 != 运算符的功能。\n派生的 PartialEq 实现了 eq 方法。当 PartialEq 在结构体上派生时，只有所有 的字段都相等时两个实例才相等，同时只要有任何字段不相等则两个实例就不相等。当在枚举上派生时，每一个成员都和其自身相等，且和其他成员都不相等。\n例如，当使用 assert_eq! 宏时，需要比较一个类型的两个实例是否相等，则 PartialEq 特征是必须的。\nEq 特征没有方法, 其作用是表明每一个被标记类型的值都等于其自身。 Eq 特征只能应用于那些实现了 PartialEq 的类型，但并非所有实现了 PartialEq 的类型都可以实现 Eq。浮点类型就是一个例子：浮点数的实现表明两个非数字（ NaN ，not-a-number）值是互不相等的。\n例如，对于一个 HashMap\u0026lt;K, V\u0026gt; 中的 key 来说， Eq 是必须的，这样 HashMap\u0026lt;K, V\u0026gt; 就可以知道两个 key 是否一样。\n次序比较的 PartialOrd 和 Ord Clone 特征用于创建一个值的深拷贝（deep copy），复制过程可能包含代码的执行以及堆上数据的复制。查阅 通过 Clone 进行深拷贝获取有关 Clone 的更多信息。\n派生 Clone 实现了 clone 方法，当为整个的类型实现 Clone 时，在该类型的每一部分上都会调用 clone 方法。这意味着类型中所有字段或值也必须实现了 Clone，这样才能够派生 Clone 。\n例如，当在一个切片（slice）上调用 to_vec 方法时， Clone 是必须的。切片只是一个引用，并不拥有其所包含的实例数据，但是从 to_vec 中返回的 Vector 需要拥有实例数据，因此， to_vec 需要在每个元素上调用 clone 来逐个复制。因此，存储在切片中的类型必须实现 Clone。\nCopy 特征允许你通过只拷贝存储在栈上的数据来复制值(浅拷贝),而无需复制存储在堆上的底层数据。查阅 通过 Copy 复制栈数据 的部分来获取有关 Copy 的更多信息。\n实际上 Copy 特征并不阻止你在实现时使用了深拷贝，只是，我们不应该这么做，毕竟遵循一个语言的惯例是很重要的。当用户看到 Copy 时，潜意识就应该知道这是浅拷贝，复制一个值会非常快。\n当一个类型的内部字段全部实现了 Copy 时，你就可以在该类型上派上 Copy 特征。 一个类型如果要实现 Copy 它必须先实现 Clone ，因为一个类型实现 Clone 后，就等于顺便实现了 Copy 。\n总之， Copy 拥有更好的性能，当浅拷贝足够的时候，就不要使用 Clone ，不然会导致你的代码运行更慢，对于性能优化来说，一个很大的方面就是减少热点路径深拷贝的发生。\n固定大小的值映射的 Hash Hash 特征允许你使用 hash 函数把一个任意大小的实例映射到一个固定大小的值上。派生 Hash 实现了 hash 方法，对某个类型进行 hash 调用，其实就是对该类型下每个字段单独进行 hash 调用，然后把结果进行汇总，这意味着该类型下的所有的字段也必须实现了 Hash，这样才能够派生 Hash。\n例如，在 HashMap\u0026lt;K, V\u0026gt; 上存储数据，存放 key 的时候， Hash 是必须的。\n默认的Default Default 特征会帮你创建一个类型的默认值。 派生 Default 意味着自动实现了 default 函数。 default 函数的派生实现调用了类型每部分的 default 函数，这意味着类型中所有的字段也必须实现了 Default，这样才能够派生 Default 。\nDefault::default 函数通常结合结构体更新语法一起使用，这在第五章的 结构体更新语法 部分有讨论。可以自定义一个结构体的一小部分字段而剩余字段则使用 ..Default::default() 设置为默认值。\n例如，当你在 Option 实例上使用 unwrap_or_default 方法时， Default 特征是必须的。如果 Option 是 None 的话, unwrap_or_default 方法将返回 T 类型的 Default::default 的结果。\n调用方法需要引入特征 在一些场景中，使用 as 关键字做类型转换会有比较大的限制，因为你想要在类型转换上拥有完全的控制，例如处理转换错误，那么你将需要 TryInto：\n1 2 3 4 5 6 7 8 9 10 11 12 13 use std::convert::TryInto; fn main() { let a: i32 = 10; let b: u16 = 100; let b_ = b.try_into() .unwrap(); if a \u0026lt; b_ { println!(\u0026#34;Ten is less than one hundred.\u0026#34;); } } 上面代码中引入了 std::convert::TryInto 特征，但是却没有使用它，可能有些同学会为此困惑，主要原因在于如果你要使用一个特征的方法，那么你需要将该特征引入当前的作用域中，我们在上面用到了 try_into 方法，因此需要引入对应的特征。\n但是 Rust 又提供了一个非常便利的办法，即把最常用的标准库中的特征通过 std::prelude 模块提前引入到当前作用域中，其中包括了 std::convert::TryInto，你可以尝试删除第一行的代码 use \u0026hellip;，看看是否会报错。\n几个综合例子 为自定义类型实现 + 操作 在 Rust 中除了数值类型的加法，String 也可以做加法，因为 Rust 为该类型实现了 std::ops::Add 特征，同理，如果我们为自定义类型实现了该特征，那就可以自己实现 Point1 + Point2 的操作:\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 use std::ops::Add; // 为Point结构体派生Debug特征，用于格式化输出 #[derive(Debug)] struct Point\u0026lt;T: Add\u0026lt;T, Output = T\u0026gt;\u0026gt; { //限制类型T必须实现了Add特征，否则无法进行+操作。 x: T, y: T, } impl\u0026lt;T: Add\u0026lt;T, Output = T\u0026gt;\u0026gt; Add for Point\u0026lt;T\u0026gt; { type Output = Point\u0026lt;T\u0026gt;; fn add(self, p: Point\u0026lt;T\u0026gt;) -\u0026gt; Point\u0026lt;T\u0026gt; { Point{ x: self.x + p.x, y: self.y + p.y, } } } fn add\u0026lt;T: Add\u0026lt;T, Output=T\u0026gt;\u0026gt;(a:T, b:T) -\u0026gt; T { a + b } fn main() { let p1 = Point{x: 1.1f32, y: 1.1f32}; let p2 = Point{x: 2.1f32, y: 2.1f32}; println!(\u0026#34;{:?}\u0026#34;, add(p1, p2)); let p3 = Point{x: 1i32, y: 1i32}; let p4 = Point{x: 2i32, y: 2i32}; println!(\u0026#34;{:?}\u0026#34;, add(p3, p4)); } 自定义类型的打印输出 在开发过程中，往往只要使用 #[derive(Debug)] 对我们的自定义类型进行标注，即可实现打印输出的功能：\n1 2 3 4 5 6 7 8 9 #[derive(Debug)] struct Point{ x: i32, y: i32 } fn main() { let p = Point{x:3,y:3}; println!(\u0026#34;{:?}\u0026#34;,p); } 但是在实际项目中，往往需要对我们的自定义类型进行自定义的格式化输出，以让用户更好的阅读理解我们的类型，此时就要为自定义类型实现 std::fmt::Display 特征：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #![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\u0026lt;u8\u0026gt;, state: FileState, } impl Display for FileState { fn fmt(\u0026amp;self, f: \u0026amp;mut fmt::Formatter) -\u0026gt; fmt::Result { match *self { FileState::Open =\u0026gt; write!(f, \u0026#34;OPEN\u0026#34;), FileState::Closed =\u0026gt; write!(f, \u0026#34;CLOSED\u0026#34;), } } } impl Display for File { fn fmt(\u0026amp;self, f: \u0026amp;mut fmt::Formatter) -\u0026gt; fmt::Result { write!(f, \u0026#34;\u0026lt;{} ({})\u0026gt;\u0026#34;, self.name, self.state) } } impl File { fn new(name: \u0026amp;str) -\u0026gt; File { File { name: String::from(name), data: Vec::new(), state: FileState::Closed, } } } fn main() { let f6 = File::new(\u0026#34;f6.txt\u0026#34;); //... println!(\u0026#34;{:?}\u0026#34;, f6); println!(\u0026#34;{}\u0026#34;, f6); } ","date":"2024-03-11T22:36:29+08:00","permalink":"https://mysetsuna.github.io/p/rust%E5%AD%A6%E4%B9%A0%E4%B9%8B%E8%B7%AF%E7%89%B9%E5%BE%81trait/","title":"Rust学习之路——特征（Trait）"},{"content":"slice（切片） 基本使用 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 /** * @param startIndex 切片开始的索引，包括 * @param endIndex 切片结束的索引，不包括 * */ Array.prototype.slice(startIndex?:number, endIndex?:number) const arr = [1, 2 ,3 ,4]; // 1.不传参数，复制数组 const a = arr.slice() // a = [1, 2, 3, 4] // 2.传入startIndex，从startIndex开始切片数组 const b = arr.slice(2) // a = [3, 4] // 3.传入startIndex，endIndex // 3.0正常传入 const d = arr.slice(2, 3) // a = [3] // 3.1 传入相同值 const c = arr.slice(2, 2) // a = [] // 3.2 endIndex比startIndex小 const c = arr.slice(2, 1) // a = [] // 3.4传入负数，从数组尾部往前推 const d = arr.slice(-2) // a = [3, 4] const d = arr.slice(-3, -1) // a = [2， 3] 在类数组对象上调用 slice() 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 // 1. 类数组对象 const arrayLike = { length: 3, 0: 2, 1: 3, 2: 4, 3: 33, // ignored by slice() since length is 3 }; console.log(Array.prototype.slice.call(arrayLike, 1, 3)); // [ 3, 4 ] // length: 3 length属性在这里被忽略, length之外的3:33也被忽略，因为超出了索引 console.log(Array.prototype.slice.call(arrayLike)) // [2, 3 ,4 ] // 2.非类数组 const someLike = { len: 3, 0: 2, 1: 3, 2: 4, 3: 33, // ignored by slice() since length is 3 }; // 没有如期调用 console.log(Array.prototype.slice.call(someLike)) // [] const someLike1 = { //length: 3 0: 2, 1: 3, 2: 4, 3: 33, // ignored by slice() since length is 3 }; // 可见需要正确的length console.log(Array.prototype.slice.call(someLike1)) // [] 使用 slice() 将类数组对象转换为数组 1 2 3 4 5 6 7 8 9 // 调用 slice() 方法时，会将 this 对象作为第一个参数传入 const slice = Function.prototype.call.bind(Array.prototype.slice); function list() { return slice(arguments); } const list1 = list(1, 2, 3); // [1, 2, 3] 在稀疏数组上使用 slice() 如果源数组是稀疏数组，slice() 方法返回的数组也会是稀疏数组。\n1 2 console.log([1, 2, , 4, 5].slice(1, 4)); // [2, empty, 4] ","date":"2024-03-08T01:10:57+08:00","image":"https://mysetsuna.github.io/p/javascript%E6%95%B0%E7%BB%84%E7%9A%84%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95slice%E5%88%87%E7%89%87/slice_hu3191306613712495.png","permalink":"https://mysetsuna.github.io/p/javascript%E6%95%B0%E7%BB%84%E7%9A%84%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95slice%E5%88%87%E7%89%87/","title":"JavaScript数组的常用方法——slice（切片）"},{"content":"This article offers a sample of basic Markdown syntax that can be used in Hugo content files, also it shows whether basic HTML elements are decorated with CSS in a Hugo theme.\nHeadings The following HTML \u0026lt;h1\u0026gt;—\u0026lt;h6\u0026gt; elements represent six levels of section headings. \u0026lt;h1\u0026gt; is the highest section level while \u0026lt;h6\u0026gt; is the lowest.\nH1 H2 H3 H4 H5 H6 Paragraph Xerum, quo qui aut unt expliquam qui dolut labo. Aque venitatiusda cum, voluptionse latur sitiae dolessi aut parist aut dollo enim qui voluptate ma dolestendit peritin re plis aut quas inctum laceat est volestemque commosa as cus endigna tectur, offic to cor sequas etum rerum idem sintibus eiur? Quianimin porecus evelectur, cum que nis nust voloribus ratem aut omnimi, sitatur? Quiatem. Nam, omnis sum am facea corem alique molestrunt et eos evelece arcillit ut aut eos eos nus, sin conecerem erum fuga. Ri oditatquam, ad quibus unda veliamenimin cusam et facea ipsamus es exerum sitate dolores editium rerore eost, temped molorro ratiae volorro te reribus dolorer sperchicium faceata tiustia prat.\nItatur? Quiatae cullecum rem ent aut odis in re eossequodi nonsequ idebis ne sapicia is sinveli squiatum, core et que aut hariosam ex eat.\nBlockquotes The blockquote element represents content that is quoted from another source, optionally with a citation which must be within a footer or cite element, and optionally with in-line changes such as annotations and abbreviations.\nBlockquote without attribution Tiam, ad mint andaepu dandae nostion secatur sequo quae. Note that you can use Markdown syntax within a blockquote.\nBlockquote with attribution Don\u0026rsquo;t communicate by sharing memory, share memory by communicating.\n— Rob Pike1\nTables Tables aren\u0026rsquo;t part of the core Markdown spec, but Hugo supports supports them out-of-the-box.\nName Age Bob 27 Alice 23 Inline Markdown within tables Italics Bold Code italics bold code A B C D E F Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus ultricies, sapien non euismod aliquam, dui ligula tincidunt odio, at accumsan nulla sapien eget ex. Proin eleifend dictum ipsum, non euismod ipsum pulvinar et. Vivamus sollicitudin, quam in pulvinar aliquam, metus elit pretium purus Proin sit amet velit nec enim imperdiet vehicula. Ut bibendum vestibulum quam, eu egestas turpis gravida nec Sed scelerisque nec turpis vel viverra. Vivamus vitae pretium sapien Code Blocks Code block with backticks 1 2 3 4 5 6 7 8 9 10 \u0026lt;!doctype html\u0026gt; \u0026lt;html lang=\u0026#34;en\u0026#34;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026#34;utf-8\u0026#34;\u0026gt; \u0026lt;title\u0026gt;Example HTML5 Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;Test\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Code block indented with four spaces \u0026lt;!doctype html\u0026gt; \u0026lt;html lang=\u0026quot;en\u0026quot;\u0026gt; \u0026lt;head\u0026gt; \u0026lt;meta charset=\u0026quot;utf-8\u0026quot;\u0026gt; \u0026lt;title\u0026gt;Example HTML5 Document\u0026lt;/title\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;p\u0026gt;Test\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Diff code block 1 2 3 4 5 [dependencies.bevy] git = \u0026#34;https://github.com/bevyengine/bevy\u0026#34; rev = \u0026#34;11f52b8c72fc3a568e8bb4a4cd1f3eb025ac2e13\u0026#34; - features = [\u0026#34;dynamic\u0026#34;] + features = [\u0026#34;jpeg\u0026#34;, \u0026#34;dynamic\u0026#34;] One line code block 1 \u0026lt;p\u0026gt;A paragraph\u0026lt;/p\u0026gt; List Types Ordered List First item Second item Third item Unordered List List item Another item And another item Nested list Fruit Apple Orange Banana Dairy Milk Cheese Other Elements — abbr, sub, sup, kbd, mark GIF is a bitmap image format.\nH2O\nXn + Yn = Zn\nPress CTRL + ALT + Delete to end the session.\nMost salamanders are nocturnal, and hunt for insects, worms, and other small creatures.\nThe above quote is excerpted from Rob Pike\u0026rsquo;s talk during Gopherfest, November 18, 2015.\u0026#160;\u0026#x21a9;\u0026#xfe0e;\n","date":"2023-09-07T00:00:00Z","permalink":"https://mysetsuna.github.io/p/markdown-syntax-guide/","title":"Markdown Syntax Guide"},{"content":"Hugo theme Stack supports the creation of interactive image galleries using Markdown. It\u0026rsquo;s powered by PhotoSwipe and its syntax was inspired by Typlog.\nTo use this feature, the image must be in the same directory as the Markdown file, as it uses Hugo\u0026rsquo;s page bundle feature to read the dimensions of the image. External images are not supported.\nSyntax 1 ![Image 1](1.jpg) ![Image 2](2.jpg) Result Photo by mymind and Luke Chesser on Unsplash\n","date":"2023-08-26T00:00:00Z","image":"https://mysetsuna.github.io/p/image-gallery/2_hu15576070775610481867.jpg","permalink":"https://mysetsuna.github.io/p/image-gallery/","title":"Image gallery"},{"content":"For more details, check out the documentation.\nBilibili video Tencent video YouTube video Generic video file Your browser doesn't support HTML5 video. Here is a link to the video instead. Gist GitLab Quote Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.\n― A famous person, The book they wrote CodeSanBox Photo by Codioful on Unsplash\n","date":"2023-08-25T00:00:00Z","image":"https://mysetsuna.github.io/p/shortcodes/cover_hu17063188895770243625.jpg","permalink":"https://mysetsuna.github.io/p/shortcodes/","title":"Shortcodes"},{"content":"Stack has built-in support for math typesetting using KaTeX.\nIt\u0026rsquo;s not enabled by default side-wide, but you can enable it for individual posts by adding math: true to the front matter. Or you can enable it side-wide by adding math = true to the params.article section in config.toml.\nInline math This is an inline mathematical expression: $\\varphi = \\dfrac{1+\\sqrt5}{2}= 1.6180339887…$\n1 $\\varphi = \\dfrac{1+\\sqrt5}{2}= 1.6180339887…$ Block math $$ \\varphi = 1+\\frac{1} {1+\\frac{1} {1+\\frac{1} {1+\\cdots} } } $$\n1 2 3 $$ \\varphi = 1+\\frac{1} {1+\\frac{1} {1+\\frac{1} {1+\\cdots} } } $$ $$ f(x) = \\int_{-\\infty}^\\infty\\hat f(\\xi),e^{2 \\pi i \\xi x},d\\xi $$\n1 2 3 $$ f(x) = \\int_{-\\infty}^\\infty\\hat f(\\xi)\\,e^{2 \\pi i \\xi x}\\,d\\xi $$ ","date":"2023-08-24T00:00:00Z","permalink":"https://mysetsuna.github.io/p/math-typesetting/","title":"Math Typesetting"}]