Using Rust
Recording some errors and refactoring encountered while writing Rust. Option When to use .as_ref()? When you want to convert an Option<T> to an Option<&T> without consuming the original Option. ...
Recording some errors and refactoring encountered while writing Rust. Option When to use .as_ref()? When you want to convert an Option<T> to an Option<&T> without consuming the original Option. ...
55: Mixing up concurrency and parallelism Concurrency enables parallelism. Concurrency provides a structure to solve a problem with parts that may be parallelized. Quote “Concurrency is about dealing with lots of things at once. Parallelism is about doing lots of things at once.” ...
4 Traits To invoke a trait method, two things must be true: The type must implement the trait. The trait must be in scope. To satisfy the latter, you may have to add a use statement for the trait: use crate::MaybeZero; This is not necessary if: The trait is defined in the same module where the invocation occurs. The trait is defined in the standard library’s prelude. The prelude is a set of traits and types that are automatically imported into every Rust program. It’s as if use std::prelude::*; was added at the beginning of every Rust module. Orphan Rule When a type is defined in another crate (e.g. u32, from Rust’s standard library), you can’t directly define new methods for it. ...
48: Panicking Panicking in Go should be used sparingly. We have seen two prominent cases, one to signal a programmer error and another where our application fails to create a mandatory dependency. 49: Ignoring when to wrap an error In general, the two main use cases for error wrapping are the following: Adding additional context to an error Marking an error as a specific error if err != nil { return fmt.Errorf("bar failed: %w", err) } This code wraps the source error to add additional context without having to create another error type. ...
42: Not knowing which type of receiver to use In Go, we can attach either a value or a pointer receiver to a method. With a value receiver, Go makes a copy of the value and passes it to the method. Any changes to the object remain local to the method. The original object remains unchanged. On the other hand, with a pointer receiver, Go passes the address of an object to the method. Intrinsically, it remains a copy, but we only copy a pointer, not the object itself (passing by reference doesn’t exist in Go). Any modifications to the receiver are done on the original object. ...
36: Not understanding the concept of a rune The len built-in function applied on a string doesn’t return the number of characters; it returns the number of bytes. What if we want to get the number of runes in a string, not the number of bytes? How we can do this depends on the encoding. In the previous example, because we assigned a string literal to s, we can use the unicode/utf8 package: ...
2 Unnecessary nested code 能够提前返回的特殊判定就提前返回 if xxx { // .... return err }else{ // do X } 总是能够写为 if xxx{ return err } // do X Make expected execution flow clear 5 Interface pollution Quote The bigger the interface, the weaker the abstraction. ...
17 Creating confusion with octal literals In Go, an integer literal starting with 0 is considered an octal integer (base 8). Note the integer literal representations in Golang: Binary - Uses a 0b or 0B prefix (for example, 0b100 is equal to 4 in base 10) Octal - Uses a 0 or 0o prefix (for example, 0o10 is equal 8 in base 10) Hexadecimal - Uses an 0x or 0X prefix (for example, 0xF is equal to 15 in base 10) Imaginary - Uses an i suffix (for example, 3i) 20 Not understanding slice length and capacity In Go, a slice is backed by an array. That means the slice’s data is stored contiguously in an array data structure. A slice also handles the logic of adding an element if the backing array is full or shrinking the backing array if it’s almost empty. ...
30: Ignoring the fact that elements are copied in range loops type account struct { balance float32 } accounts := []account{ {balance: 100.}, {balance: 200.}, {balance: 300.}, } for _, a := range accounts { a.balance += 1000 } // Output: [{100} {200} {300}] In this example, the range loop does not affect the slice’s content. In Go, everything we assign is a copy: ...
Using Error Wrapping An error often “bubbles up” a call chain of multiple functions. In other words, a function receives an error and passes it back to its caller through a return value. The caller might do the same, and so on, until a function up the call chain handles or logs the error. An error can be “wrapped” around another error using fmt.Errorf() and the special formatting verb %w. ...
这里简单总结一下三种组合方式: interface 中嵌套 interface struct 中嵌套 struct struct 中嵌套 interface interface 中嵌套 interface 这种组合方式体现了接口隔离原则(ISP)和接口组合原则, 通过接口嵌套,我们可以构建更大的接口 // 基础读接口 type Reader interface { Read(p []byte) (n int, err error) } // 基础写接口 type Writer interface { Write(p []byte) (n int, err error) } // 组合接口 type ReadWriter interface { Reader // 嵌套 Reader 接口 Writer // 嵌套 Writer 接口 } struct 中嵌套 struct 这种组合方式体现了组合优于继承12的原则,是 Go 中实现代码复用的重要方式 ...
05 Vectors fn array_and_vec() -> ([i32; 4], Vec<i32>) { let a = [10, 20, 30, 40]; // Array // TODO: Create a vector called `v` which contains the exact same elements as in the array `a`. // Use the vector macro. // let v = ??? (a, v) } 这里有几种写法,首先想到的肯定是用一个类似于 for 循环的结构类循环赋值 这里总结一下 Rust 中常见的迭代器 for item in list 会调用 into_iter(),消耗 list 的所有权 for item in &list 会调用 iter(),遍历 list 的引用 for item in &mut list 会调用 iter_mut(),遍历 list 的可变引用 如果想要获取 index, 可以使用 enumerate() 方法: ...