Summary of some minor problems encountered while writing Rust

Automatic dereference (dereference coercion)

rust 复制代码
struct Car {
    width: u32,
    height: u32,
}

fn main() {
    let mut car1 = Car {
        width: 100,
        height: 200,
    };
    
    let car2 = &mut car1;
    car2.width = 200;
    println!("width: {}", car2.width);

    (*car2).width = 300;
    println!("width: {}", car2.width);
}

car2 is a mutable reference to car1. When we try to change width by car2, Rust will automatically dereference car2 to get the car1 instance and then modify the value of width. So car2.width = 200; is equal to (*car2).width = 300;.

But things are not so simple. We can see the following example. The first part is right. But for the second part, if we use y = 10, we will get an error. This is because the automatic deference doesn't occur on primitive data types. So we must use * explicitly.

rust 复制代码
let car2 = &mut car1;
car2.width = 200;
println!("width: {}", car2.width);

let mut x = 5;
let y = &mut x;
*y = 10;
println!("y: {}", y);

Why? This may be a design consideration. For struct, it has many elements, so automatic dereference can make code simpler and more readable. But for primitive data types, if we don't use *, it can make people misunderstand that y is reassigned rather than that the value which y refers to is changed.

And I need to add some things. * doesn't get the ownership and just can calculate or other things.

There is another situation. It can work but this isn't automatic dereference. This depends on + which has an Add trait.

rust 复制代码
let a = 12;
let b = &a;
let c = &a;
println!("{}", b + c);

parse()

It can change the type of the value.

We must specify its type when using parse().

rust 复制代码
let a: i32 = (&a[1..]).parse().unwrap();

When this value will return, we don't need to specify its type. Because the function signature is already specified. But I think we should still do this, which makes the code clearer.

collect()

It can take the iterator value and convert them to Vec or other things.

We must specify its type when using collect(). This is because that it can handle not only Vec but also other things like HashMap.

rust 复制代码
let cells: Vec<_> = first_second.spilt('_').collect();

Using mutable objects continuously

According to borrow rules, we can't have mutable and immutable borrow at the same time. This value is mutable borrow from self. And self.double_get continues to borrow self. So this is a wrong way, which could lead to the data race.

rust 复制代码
for (key, value) in self.real_data.iter_mut() {
  *value = self.double_get("get".to_string() + key.as_str());
}

Other example:

rust 复制代码
match boxes.get_mut(other) {
  Some(v) => {
	parse_command(v, turtle, variables, boxes);
  }
  None => {
	return Err(logo::CommandError::WrongCommandErr(other.to_string()));
  }
}

unwrap() VS except() VS ?

For Result:
unwrap(): If Result is Err(), the program directly panic(); If Result is Ok(), it will return the value in Ok().
except(): It is similar to unwrap(), except that it can panic() custom errors.
?: If Result is Err(), it will return Err(); If Result is Ok(), it will return the value in Ok().

For Option():
unwrap(): If Option is None, the program directly panic(); If Option is Some(), it will return the value in Ok().
except(): It is similar to unwrap(), except that it can panic() custom errors.
?: If Option is None, it will return None; If Option is Some(), it will return the value in Some().

Of course, we can also use match for more detailed processing.

match VS if let VS let if

rust 复制代码
let f = match f {
	Ok(file) => file,
	Err(error) => {
		panic!("Problem opening the file: {:?}", error)
	},
};

if let 1 = 1 {
	println!("1");
}

let a = if true {  // This must be a bool value
	1
} else {
	2
};

Why does Rust prohibit declaring multiple mutable references or mutable and immutable references

There could be many reasons. But I just explain two.

For the data race, suppose you declare one mutable reference and an immutable reference. You use mutable references to change the original data. When you use an immutable references to read the same data, you will find that the data has changed. But this is immutable reference.

For memory security, suppose you declare one mutable reference and one immutable reference. If you delete all the data by mutable reference. If you use immutable reference to read the data, they will have the problem.

The mut and &mut in the function signature

rust 复制代码
fn conmand(mut turtle: Turtle, variables: &mut variables)

iter() VS iter_mut() VS into_iter()

rust 复制代码
// iter() can get the immutable iterator of a collection.
let vec = vec![1, 2, 3, 4];
for value in vec.iter() {
    println!("{}", value);
}

// iter_mut() can get the mutable iterator of a collection. It applies to change a collection.
let mut vec = vec![1, 2, 3, 4];
for value in vec.iter_mut() {
    *value += 10;
}

// into_iter() can get the ownership.
let vec = vec![1, 2, 3, 4];
let new_vec: Vec<_> = vec.into_iter().map(|x| x * 10).collect();

The struct of the struct

When we use the struct like the following, we must use VariablesCommand::Make{name, value}. That means we can't use directly a specific value.

rust 复制代码
pub enum VariablesCommand {
    Make { name: String, value: f32 },
    AddAssign { name: String, value: f32 },
}

the pattern matching of map()

As you can see, x is &&strbut &x is &str. Actually, this &is pattern matching rather than reference.

the usage of slice

Whether it's &str or String, you have to use it by & when you use slice.

rust 复制代码
let a = "abcdedf";
let b = "abcdedf".to_string();
let c = &a[1..];
let d = &b[1..];
println!("c: {}", c);
println!("d: {}", d);

points to note when using Hashmap()

First, when you use get() or remove(), you must use & like the following. Because you don't have to take the ownership and the reference is more reasonable.

rust 复制代码
hashmap.remove(&i);

Second, the key of the Hashmap doesn't support the float due to some security reasons. Java or Python supports that.

points to note when using Vec()

Vec() uses usize.

points to note when using match

The following example is wrong because the elements of the match need to have the same return value.

rust 复制代码
let result = match value {
  0 => "zero",
  1 => 1,
  _ => "many",
};

one example understanding trait

rust 复制代码
pub enum Die {
    D4,
    D6,
    D8,
    D10,
    D12,
    D20,
}

pub struct Coin;

pub trait Rollable {
    fn roll(&self) -> String;
}

impl Rollable for Die {
    fn roll(&self) -> String {
        let max = match self {
            Die::D4 => 4,
            Die::D6 => 6,
            Die::D8 => 8,
            Die::D10 => 10,
            Die::D12 => 12,
            Die::D20 => 20,
        };
        get_random_value(max).to_string()
    }
}

impl Rollable for Coin {
    fn roll(&self) -> String {
        let max = match self {
            Coin => 2,
        };
        get_random_value(max).to_string()
    }
}

pub fn roll<T: Rollable> (item: T) -> u8 {
    item.roll().parse().unwrap()
}

the passing of reference in the struct

Why does person.greetings need &? person: &Person indicates that person is a reference, which is immutable. But person.greetings will get Box<dyn Greeting> rather than &Box<dyn Greeting>. This is because person: &Person can only indicate that person.greetings is immutable. But it can't indicate that person.greetings is &. So the mutability is passable but the reference is not.

rust 复制代码
struct Person {
    name: String,
    greetings: Vec<Box<dyn Greeting>>,
}
fn speak_all_greetings(person: &Person) {
    println!("{} says:", person.name);
    for greeting in &person.greetings {
        greeting.greet();
    }
}

dyn

Why use dyn? This indicates that the interface (trait) is polymorphic, which means it can reference different types of instances that implement a particular trait.

rust 复制代码
struct English;
struct Spanish;
struct French;

trait Greeting {
    fn greet(&self);
}

impl Greeting for English {
    fn greet(&self) {
        println!("Hello!");
    }
}

impl Greeting for Spanish {
    fn greet(&self) {
        println!("Hola!");
    }
}

impl Greeting for French {
    fn greet(&self) {
        println!("Bonjour!");
    }
}

struct Person {
    name: String,
    greetings: Vec<Box<dyn Greeting>>,
}

the case without

<T> already apeared after the impl. So new doesn't need to declare <T>.

rust 复制代码
impl<T> Point<T> {
    pub fn new (x: T, y: T) -> Self {
        Point { x, y }
    }
}

the signature of <T> and Box for closure

f is a closure. Why can't we just use FnOnce(i32) -> i32 instead of <T>. Because when declaring the signature, Rust needs to know its exact size, and Fn(i32) -> i32 size is uncertain. So actually we can use Box which is dynamic distribution but it will have performance loss. The following way is static distribution.

And why we can't use T: Fn(i32) -> i32 + Fn(i64) -> i64? Because Fn stands for closure and a closure just can have one trait about closure.

rust 复制代码
impl MyOption {
    fn map<T>(self, f: T) -> MyOption
    where
        T: FnOnce(i32) -> i32,
    {
        match self {
            MyOption::Some(x) => MyOption::Some(f(x)),
            MyOption::None => MyOption::None,
        }
    }
}
相关推荐
临风细雨35 分钟前
从 Bun 的 Rust 重写,看 C# 如何重建 AI 基础设施层
人工智能·rust·c#
x-cmd1 小时前
Linus 9 年前说「模糊测试有效」——今天已成 OSS-Fuzz 时代铁律
linux·ai·rust·开源·自动化·agent·安全性测试
CodeForWater2 小时前
RUST设置临时环境变量在PowerShell
rust
独孤留白3 小时前
从C到Rust:Trait Deref + DerefMut 智能指针与自动解引用
rust
程序员爱钓鱼4 小时前
Rust 数据类型详解:整数、浮点数、布尔、字符、元组和数组
后端·rust
梦帮科技5 小时前
Rust+Tauri+EVM:构建下一代内存安全级数字版权(DRM)主权音频网络与跨链金融系统的技术实践与全景架构
网络·安全·架构·rust·区块链·音视频·web3.py
明理的信封16 小时前
AI 基础设施的“去 Python 化“:Rust 与 C# 的两条替代路径
人工智能·python·rust
doiito1 天前
【Agent Harness】Gliding Horse v0.1.4.preview 发布:时间感知、闭环审计与智能增强
ai·rust·架构设计·系统设计·ai agent
独孤留白1 天前
从C到Rust:堆内存,数据如何逃逸栈的约束?
rust
doiito(Do It Together)1 天前
【安全,架构】RustyVault 项目深度分析报告以及和HashiCorp Vault的差异
微服务·架构·rust