Rust小练习题

Rust小练习题

原文链接:https://ovea-y.cn/rust_rustlings_finish0/

练习题来源:https://github.com/rust-lang/rustlings

exercises/variables

       x = 5;
       --------------------------
       let x = 5;
   let x;
   -----------------------------
   let x = 10;
   let x = 3;
   --------------------
   let mut x = 3;

原因,默认let是定义常量,无法重新赋值。

   let x: i32;
   -----------------------
   let x: i32 = 0;

未初始化的变量无法使用。

   number = 3;
   -------------------------
   let number0 = 3;

常量和变量在确定类型后,中途都无法改变。

exercises/if

  1. 在bigger方法中添加下面这个语句即可

    return if a > b {a} else {b};
    

exercises/functions

   fn call_me() {}

总之,就是在main外面定义一个调用用的私有方法。

   fn call_me(num)
   -------------------------------
   fn call_me(num: u32)

方法传入的参数需要指定类型

   call_me();
   --------------------------------
   call_me(4);

需要传参的方法需要传入参数

   fn sale_price(price: i32) -> 
   --------------------------------
   fn sale_price(price: i32) -> i32

方法需要定义返回值

   num * num;
   ------------------
   num * num

将分号去掉,变成表达式,会自动返回值。

exercises/primitive_types

   let
   -------------
   let is_evening = false;

完成代码即可。

   let your_character = '1';
   ==========================
   let your_character = 'C';
   ==========================
   let your_character = ' ';
   let a = [123, 32, 6, 45];
   let nice_slice = &a[1..4];

注意!切片中的数字表示在数组指定下标处前面进行切割,所以a[4]前面切割,就把4和5分离了。

   let (name, age) = cat;

其实这里我是猜的,因为看起来是把cat元组的值,分别放在name和age中,正好我用过的python有类似的语法,所以就这么完成了~

   println!("The second number is {}", numbers.1);

其实我一开始使用nambers[1],但是错误提示告诉我元组要用numbers.1进行索引。所以对于元组的操作,我已经基本了解了。

exercises/structs

https://doc.rust-lang.org/rust-by-example/custom_types.html

根据这里进行学习和完成,这里无法使用其他语言的经验了。

   // structs1.rs
   // Address all the TODOs to make the tests pass!
   struct ColorClassicStruct<'a> {
       // TODO: Something goes here
       name: &'a str,
       hex: &'a str
   }
   
   struct ColorTupleStruct<'a>(/* TODO: Something goes here */&'a str, &'a str);
   
   #[derive(Debug)]
   struct UnitStruct;
   
   #[cfg(test)]
   mod tests {
       use super::*;
   
       #[test]
       fn classic_c_structs() {
           // TODO: Instantiate a classic c struct!
           // let green =
           let green = ColorClassicStruct {
               name: "green", 
               hex: "#00FF00"
           };
   
           assert_eq!(green.name, "green");
           assert_eq!(green.hex, "#00FF00");
       }
   
       #[test]
       fn tuple_structs() {
           // TODO: Instantiate a tuple struct!
           // let green =
           let green = ColorTupleStruct("green", "#00FF00");
   
           assert_eq!(green.0, "green");
           assert_eq!(green.1, "#00FF00");
       }
   
       #[test]
       fn unit_structs() {
           // TODO: Instantiate a unit struct!
           // let unit_struct =
           let unit_struct = UnitStruct{};
           let message = format!("{:?}s are fun!", unit_struct);
   
           assert_eq!(message, "UnitStructs are fun!");
       }
   }
   
   let your_order = Order {
       name: String::from("Hacker in Rust"),
       count: 1,
       ..order_template
   };

以order_template为模板,只改变name和count创建结构体。

exercises/strings

   "blue"
   --------------------------
   String::from("blue")

将&str类型转换为String类型

  1. 参考来源https://stackoverflow.com/questions/23975391/how-to-convert-a-string-into-a-static-str

    问题记录:how to convert a String to a &str in Rust?

    解决方法:利用切片将String转换成&str

    if is_a_color_word(&word[..])
    

exercises/enums

   enum Message {
       // TODO: define a few types of messages as used below
       Quit, Echo, Move, ChangeColor
   }

和其他语言一样的方式定义枚举类型

   enum Message {
       // TODO: define the different variants used below
       Move {x: i32, y: i32},
       Echo(String),
       ChangeColor(i32, i32, i32),
       Quit
   }

这个用法,大部分都猜对了~

   enum Message {
       // TODO: implement the message variant types based on their usage below
       Move {x: u8, y: u8},
       Echo(String),
       ChangeColor(u8, u8, u8),
       Quit
   }
   
   impl State {
       fn process(&mut self, message: Message) {
           // TODO: create a match expression to process the different message variants
           match message {
               Message::ChangeColor (x, y, z) => {
                   self.change_color((x, y, z));
               },
               Message::Echo (x) => {
                   self.echo(x);
               },
               Message::Move{x, y} => {
                   self.move_position(Point{x: x, y: y});
               },
               Message::Quit => {
                   self.quit();
               }
           }
       }
   }

这里花了挺多时间学习match的语法,比如impl可以用self指代自己这个对线,类似java的this,但是更像python的写法。然后匹配后,如果要取出值,元组用(),结构体用{},普通的则什么都不需要加。

exercises/tests

   assert!(12 == 12);

调试用的语句,里面的值应该是一个布尔值。

   assert_eq!(12, 12);

判断两遍是否相等

   #[cfg(test)]
   mod tests {
       use super::*;
   
       #[test]
       fn is_true_when_even() {
           assert!(is_even(6));
       }
   }

测试方法返回的bool值

exercises/modules

   fn make_sausage()
   ----------------------------
   pub fn make_sausage()

将私有方法变成公有的

       pub use self::fruits::PEAR as fruit;
       pub use self::veggies::CUCUMBER as veggie;

给成员加上pub,扩大权限即可。我在这稍微花了些时间弄清楚pub应该放在什么位置。

exercises/macros

   my_macro();
   --------------
   my_macro!();

将方法调用改成宏调用

  1. 宏要在调用之前定义,把下面这段移到main方法上面。

    macro_rules! my_macro {
        () => {
            println!("Check out my macro!");
        };
    }
    
   #[macro_use]
   mod macros {
       macro_rules! my_macro {
           () => {
               println!("Check out my macro!");
           };
       }
   }

定义了宏的模块,需要加上#[macro_use]才能使用

   macro_rules! my_macro {
       () => {
           println!("Check out my macro!");
       };
       ($val:expr) => {
           println!("Look at this other macro: {}", $val);
       };
   }
   

两个宏实现后面没加分号

exercises/move_semantics

  1. Vec也要声明成变量才能进行修改。

   let mut vec1 = fill_vec(vec0);
   ------------------
   let mut vec1 = vec0.clone();

将移动改成克隆。

   fn fill_vec(mut vec: Vec<i32>) -> Vec<i32> {
       vec.push(22);
       vec.push(44);
       vec.push(66);
   
       vec
   }
   

在方法参数列表,就设置vec为变量。

   fn main() {
       let mut vec1 = fill_vec();
   
       println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
   
       vec1.push(88);
   
       println!("{} has length {} content `{:?}`", "vec1", vec1.len(), vec1);
   }
   
   // `fill_vec()` no longer take `vec: Vec<i32>` as argument
   fn fill_vec() -> Vec<i32> {
       let mut vec = Vec::new();
   
       vec.push(22);
       vec.push(44);
       vec.push(66);
   
       vec
   }
   

总之就是重构这个方法,这样就不用再定义一个没用的vue0创建了。

exercises/error_handling

  1. 其实此处是讲解一个关于函数编写的问题。

  2. 此处参照https://doc.rust-lang.org/book/ch09-02-recoverable-errors-with-result.html#a-shortcut-for-propagating-errors-the--operator

    当方法加入了?操作符时,如果出现错误,将会自动返回出错的信息。

    let qty = item_quantity.parse::<i32>()?;
    
  3. 此题其实是让main具有返回值,这样可以抛出异常,或者正常结束时返回0。

    fn main() -> Result<i32, ParseIntError> {
        let mut tokens = 100;
        let pretend_user_input = "8";
    
        let cost = total_cost(pretend_user_input)?;
    
        if cost > tokens {
            println!("You can't afford that many!");
        } else {
            tokens -= cost;
            println!("You now have {} tokens.", tokens);
        }
    
        Ok(0)
    }
    
   fn read_and_validate(b: &mut dyn io::BufRead) -> Result<PositiveNonzeroInteger, Box<dyn error::Error>> {
       let mut line = String::new();
       b.read_line(&mut line)?;
       let num: i64 = line.trim().parse()?;
       let answer = PositiveNonzeroInteger::new(num)?;
       Ok(answer)
   }

里面可能产生多种类型的异常,都在可能产生错误的方法后面加上问号,返回的错误类型不止一种,所以填入调用这个函数的函数返回的异常类型,即Box<dyn error::Error>,需要注意的是,answer需要用Ok返回,这里Ok的k是小写,不是大写!我在这里被坑了很久。

errorsn.rs

  1. result1.rs

    impl PositiveNonzeroInteger {
        fn new(value: i64) -> Result<PositiveNonzeroInteger, CreationError> {
            if (value < 0) {
                Err(CreationError::Negative)
            } else if(value == 0) {
                Err(CreationError::Zero)
            } else {
                Ok(PositiveNonzeroInteger(value as u64))
            }
        }
    }
    

    根据不同的情况,返回值或异常。

exercises/clippy

   if (y - x).abs() > 0.0

按照clippy的提示,将代码改成了这样,因为浮点数不精确,不能直接判断是否相等。

   if let Some(x) = option {
   	res += x;
   }

按照clippy的提示,将代码改成了这样,相当于将Option类型变成普通的数值类型。

exercises/option

   fn main() {
       print_number(Some(13));
       print_number(Some(99));
   
       let mut numbers: [Option<u16>; 5] = [Some(0), Some(0), Some(0), Some(0), Some(0)];
       for iter in 0..5 {
           let number_to_add: u16 = {
               ((iter * 5) + 2) / (4 * 16)
           };
   
           numbers[iter as usize] = Some(number_to_add);
       }
   }

这里主要修改main中的数据类型。

  1. print_number需要传入一个Option<u16>类型的数据,所以需要用Some进行包装。

  2. 需要初始化数组numbers。

  3. 需要将iter改为usize类型。

  4. 需要封装number_to_add

   fn main() {
       let optional_value = Some(String::from("rustlings"));
       // Make this an if let statement whose value is "Some" type
       if let Some(ref value) = optional_value {
           println!("the value of optional value is: {}", value);
       } else {
           println!("The optional value doesn't contain anything!");
       }
   
       let mut optional_values_vec: Vec<Option<i8>> = Vec::new();
       for x in 1..10 {
           optional_values_vec.push(Some(x));
       }
   
       // make this a while let statement - remember that vector.pop also adds another layer of Option<T>
       // You can stack `Option<T>`'s into while let and if let
       if let Some(ref value) = optional_values_vec.pop() {
           println!("current value: {}", value.unwrap());
       }
   }
 这里主要是if let的用法。
  1. 如果是从Option类型中取出有效值并判断,使用if let Some(ref value),有效值会被放入value中。
  2. Vue pop的就是Option类型,再加上它原本就是存储Option类型,所以取出Value后,还需要unwarap。

exercises/standard_library_types

   fn main() {
       let numbers: Vec<_> = (0..100u32).collect();
       let shared_numbers = Arc::new(numbers); // TODO
       let mut joinhandles = Vec::new();
   
       for offset in 0..8 {
           let child_numbers = shared_numbers.clone();
           joinhandles.push(thread::spawn(move || {
               let mut i = offset;
               let mut sum = 0;
               while i < child_numbers.len() {
                   sum += child_numbers[i];
                   i += 5;
               }
               println!("Sum of offset {} is {}", offset, sum);
           }));
       }
       for handle in joinhandles.into_iter() {
           handle.join().unwrap();
       }
   }
   
   pub fn capitalize_first(input: &str) -> String {
       let mut c = input.chars();
       let mut result = String::new();
       while let Some(first) = c.next() {
           if (result.len() == 0) {
               result.push(first.to_ascii_uppercase());
           } else {
               result.push(first);
           }
       }
       return result;
       // match c.next() {
       //     None => String::new(),
       //     Some(first) => first.collect::<String>() + c.as_str(),
       // }
   }
   
   #[cfg(test)]
   mod tests {
       use super::*;
   
       // Step 1.
       // Tests that verify your `capitalize_first` function implementation
       #[test]
       fn test_success() {
           assert_eq!(capitalize_first("hello"), "Hello");
       }
   
       #[test]
       fn test_empty() {
           assert_eq!(capitalize_first(""), "");
       }
   
       // Step 2.
       #[test]
       fn test_iterate_string_vec() {
           let words = vec!["hello", "world"];
           let capitalized_words: Vec<String> = words.iter().map(|x| capitalize_first(x)).collect(); // TODO
           assert_eq!(capitalized_words, ["Hello", "World"]);
       }
   
       #[test]
       fn test_iterate_into_string() {
           let words = vec!["hello", " ", "world"];
           let mut capitalized_words = String::new(); // TODO
           for word in words.iter() {
               capitalized_words.push_str(&capitalize_first(word));
           }
           assert_eq!(capitalized_words, "Hello World");
       }
   }
   

只要按照步骤进行就可以了。

  1. 挑战一部分,很简单,根据要求,针对不同的除法情况进行不同的处理即可。

    // This function should calculate `a` divided by `b` if `a` is
    // evenly divisible by b.
    // Otherwise, it should return a suitable error.
    pub fn divide(a: i32, b: i32) -> Result<i32, DivisionError> {
        if b == 0 {
            Err(DivisionError::DivideByZero)
        } else if a % b != 0 {
            Err(DivisionError::NotDivisible(NotDivisibleError{dividend: a, divisor: b}))
        } else {
            Ok(a / b)
        }
    }
    

    第二部分,难点在于分清楚类型之间的转换。

        // Iterator exercises using your `divide` function
        #[test]
        fn result_with_list() {
            let numbers = vec![27, 297, 38502, 81];
            let division_results = numbers.into_iter().map(|n| divide(n, 27));
            let x : Result<Vec<i32>, DivisionError> = Ok(division_results.map(|n| n.unwrap()).collect()); //... Fill in here!
            assert_eq!(format!("{:?}", x), "Ok([1, 11, 1426, 3])");
        }
    
        #[test]
        fn list_of_results() {
            let numbers = vec![27, 297, 38502, 81];
            let division_results = numbers.into_iter().map(|n| divide(n, 27));
            let x : Vec<Result<i32, DivisionError>> = division_results.collect(); //... Fill in here!
            assert_eq!(format!("{:?}", x), "[Ok(1), Ok(11), Ok(1426), Ok(3)]");
        }
    
   pub fn factorial(num: u64) -> u64 {
       // Complete this function to return factorial of num
       // Do not use:
       // - return
       // For extra fun don't use:
       // - imperative style loops (for, while)
       // - additional variables
       // For the most fun don't use:
       // - recursion
       // Execute `rustlings hint iterators4` for hints.
       if (num == 1) {
           1
       } else {
           num * factorial(num - 1)
       }
   }

对于我来说,这个测试是这里面最简单的……

exercises/traits

   impl AppendBar for String {
       //Add your code here
       fn append_bar(self) -> Self {
           self + "Bar"
       }
   }
   

就是实现一个简单的traits,让所有String具备append_bar方法,效果是为原来的字符串添加Bar

   //TODO: Add your code here
   impl AppendBar for Vec<String> {
       fn append_bar(self) -> Self {
           let mut tmp = self;
           tmp.push(String::from("Bar"));
           return tmp;
       }
   }

此处首先需要让self变为可修改,然后在添加Bar后返回。

exercises/generic

   let mut shopping_list: Vec<&str> = Vec::new();

把?换成&str表示存储字面量

   // struct Wrapper<u32> {
   //     value: u32
   // }
   
   struct Wrapper<T> {
       value: T
   }
   
   // impl<u32> Wrapper<u32> {
   //     pub fn new(value: u32) -> Self {
   //         Wrapper { value }
   //     }
   // }
   
   impl<T> Wrapper<T> {
       pub fn new(value: T) -> Self {
           Wrapper { value }
       }
   }

将Wrapper的类型改为泛型即可。别忘了删除store_str_in_wrapper中的assert!(false);

   impl ReportCard {
       pub fn print(&self) -> String {
           if self.grade < 2.5 {
               return format!("{} ({}) - achieved a grade of {}", &self.student_name, &self.student_age, "A+");
           } else if self.grade < 3.0 {
               return format!("{} ({}) - achieved a grade of {}", &self.student_name, &self.student_age, "B+");
           } else if self.grade < 3.5 {
               return format!("{} ({}) - achieved a grade of {}", &self.student_name, &self.student_age, "C+");
           } else if self.grade < 4.0 {
               return format!("{} ({}) - achieved a grade of {}", &self.student_name, &self.student_age, "D+");
           } else if self.grade < 4.5 {
               return format!("{} ({}) - achieved a grade of {}", &self.student_name, &self.student_age, "E+");
           }
           return format!("{} ({}) - achieved a grade of {}", &self.student_name, &self.student_age, String::from("F-"));
       }
   }

总之,把分数转换成字母评分就可以了~

后来发现好像理解错题目意思了,除了把第二个测试的分数改成字母以外,最后修改如下。

pub struct ReportCard<T: std::fmt::Display> {
    pub grade: T,
    pub student_name: String,
    pub student_age: u8,
}

impl<T: std::fmt::Display> ReportCard<T> {
    pub fn print(&self) -> String {
        format!("{} ({}) - achieved a grade of {}", &self.student_name, &self.student_age, &self.grade)
    }
}

只用T的时候报了错误,并给出了提示'T' doesn't implement 'std::fmt::Display',所以就限定了类型。

exercises/threads

终于到了线程部分!

fn main() {
    let status = Arc::new(Mutex::new(JobStatus { jobs_completed: 0 }));
    let status_shared = status.clone();
    thread::spawn(move || {
        for _ in 0..10 {
            thread::sleep(Duration::from_millis(250));
            status_shared.lock().unwrap().jobs_completed += 1;
        }
    });
    while status.lock().unwrap().jobs_completed < 10 {
        println!("waiting... ");
        thread::sleep(Duration::from_millis(500));
    }
}

使用锁(Mutex,RwLock)才能在其他线程进行变量修改。

Arc<T>是指共享T的所有权。当调用clone之后,会产生一个新的指针指向堆空间同一个结构,当最后一个Arc指针被销毁时,这部分堆内存才会被回收。

exercises/conversions

终于到最后一部分了~

   total / values.len()
   -----------------------------
   total / values.len() as f64

类型转换……

   // Steps:
   // 1. If the length of the provided string is 0, then return the default of Person
   // 2. Split the given string on the commas present in it
   // 3. Extract the first element from the split operation and use it as the name
   // 4. Extract the other element from the split operation and parse it into a `usize` as the age
   // If while parsing the age, something goes wrong, then return the default of Person
   // Otherwise, then return an instantiated Person onject with the results
   impl From<&str> for Person {
       fn from(s: &str) -> Person {
           if s.len() == 0 {
               return Person::default();
           }
           let t : Vec<&str> = s.split(",").collect();
           if let Ok(age1) = t[1].parse::<usize>() {
               Person{name: t[0].to_string(), age: age1}
           } else {
               Person::default()
           }
           
       }
   }
需要注意parse转换出错的情况。
   // Your task is to complete this implementation
   // in order for the line `let p = Person::try_from("Mark,20")` to compile
   // and return an Ok result of inner type Person.
   // Please note that you'll need to parse the age component into a `usize`
   // with something like `"4".parse::<usize>()`. The outcome of this needs to
   // be handled appropriately.
   //
   // Steps:
   // 1. If the length of the provided string is 0, then return an error
   // 2. Split the given string on the commas present in it
   // 3. Extract the first element from the split operation and use it as the name
   // 4. Extract the other element from the split operation and parse it into a `usize` as the age
   // If while parsing the age, something goes wrong, then return an error
   // Otherwise, then return a Result of a Person object
   impl TryFrom<&str> for Person {
       type Error = String;
       fn try_from(s: &str) -> Result<Self, Self::Error> {
           if s.len() == 0 {
               return Err(String::from("Err happen!"));
           }
           let t : Vec<&str> = s.split(",").collect();
           if let Ok(age1) = t[1].parse::<usize>() {
               Ok(Person{name: t[0].to_string(), age: age1})
           } else {
               Err(String::from("Err happen!"))
           }
       }
   }

和之前那个一样,只不过这里是返回错误,不是返回默认的Person。

  1. 参考此处信息*https://doc.rust-lang.org/std/convert/trait.AsRef.html*,其他类似信息*https://doc.rust-lang.org/std/convert/trait.AsMut.html*。

    // Obtain the number of bytes (not characters) in the given argument
    // Add the AsRef trait appropriately as a trait bound
    fn byte_counter<T: AsRef<str>>(arg: T) -> usize {
        arg.as_ref().as_bytes().len()
    }
    
    // Obtain the number of characters (not bytes) in the given argument
    // Add the AsRef trait appropriately as a trait bound
    fn char_counter<T: AsRef<str>>(arg: T) -> usize {
        arg.as_ref().chars().count()
    }
    
    

    其实也算是“教学关”。

   // Steps:
   // 1. If the length of the provided string is 0, then return an error
   // 2. Split the given string on the commas present in it
   // 3. Extract the first element from the split operation and use it as the name
   // 4. Extract the other element from the split operation and parse it into a `usize` as the age
   // If while parsing the age, something goes wrong, then return an error
   // Otherwise, then return a Result of a Person object
   impl FromStr for Person {
       type Err = String;
       fn from_str(s: &str) -> Result<Person, Self::Err> {
           if s.len() == 0 {
               Err(String::from("Format error!"))
           } else {
               let t : Vec<&str> = s.split(",").collect();
               if let Ok(age1) = t[1].parse::<usize>() {
                   Ok(Person{name: t[0].to_string(), age: age1})
               } else {
                   Err(String::from("Format error!"))
               }
           }
       }
   }

和上一个之前的前两个大同小异~

测验

test1

先定义一个方法calculate_apple_price,然后根据题目要求写就完成了~

fn calculate_apple_price(count: u32) -> u32 {
    if (count > 40) {
        count
    } else {
        count * 2
    }
}

test2

string_slice("blue");
string("red".to_string());
string(String::from("hi"));
string("rust is fun!".to_owned());
string("nice weather".into());
string(format!("Interpolation {}", "Station"));
string_slice(&String::from("abc")[0..1]);
string_slice("  hello there ".trim());
string("Happy Monday!".to_string().replace("Mon", "Tues"));
string("mY sHiFt KeY iS sTiCkY".to_lowercase());

没想到一次通过了!~ 这的测试主要是用来考察对&str和String的判断。

test3

自己补充和完成一个测试。

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn returns_twice_of_positive_numbers() {
        assert_eq!(times_two(4), 8);
    }

    #[test]
    fn returns_twice_of_negative_numbers() {
        // TODO write an assert for `times_two(-4)`
        assert_eq!(times_two(-4), -8)
    }
}

test4

总之,就是自己编写一个宏。

macro_rules! my_macro {
    ($s:expr) => {
        format!("Hello {}", $s);
    };
}

完成!

Pasted image 20240120233829

原文链接:https://ovea-y.cn/rust_rustlings_finish0/

Read more

香港银行开户指南

香港银行开户指南

注意:本文所有内容,都是需要前往香港的情况下才能使用! 本文主要介绍2家实体银行和3家虚拟银行! 实体银行包含: * 中国银行(香港) * 汇丰银行(香港),该银行也被称为“香港上海汇丰银行” 虚拟银行包含: * 众安银行 * 天星银行 * 蚂蚁银行 1、各银行所需资料和办理方式 银行名称 办理方式 所需材料 & 要求 备注 汇丰银行(香港) 提前预约,线下到营业点办理 (如果没预约,只能很早排队取线下号,不一定能取到) 必备证件: 1. 港澳通行证 2. 入境海关小票 3. 身份证 投资证明: 1. 证券App近三月股票交割单 2. 中国结算近三月交割单 3. 银行近三月流水单 4. 支付宝投资流水 资产证明: 1. 银行资产证明 2.

By 夕元
C++协程(Coroutines)

C++协程(Coroutines)

原文链接:https://ovea-y.cn/cpp_coroutine_20/ C++协程(Coroutines) 1. 简单介绍协程 协程可以简单的理解为,它是一个可以随时“中断”,并再次恢复执行的函数。 C++协程框架的特点: * 无栈协程 * 非对称设计(开发者可以自行设计协程调度器,做成对称设计) 2. 协程和函数的区别 函数:函数调用是线性、同步、一次性的执行模式,调用者必须等待被调用方法执行完成后返回。 协程:协程可以异步执行,调用者不需要等待协程完成,可以在协程挂起时继续做其他事情。在执行过程中通过特定的语法(co__yield_和_co__await)暂停执行,并在之后的某个时刻恢复执行。 2.1 普通函数的执行过程 一个普通函数在执行的时候,主要包含两个操作,分别是调用(call)和返回(return)

By 夕元
GitHub Workflows

GitHub Workflows

原文链接:https://ovea-y.cn/github_workflows/ 本文介绍GitHub工作流的创建和作用。 工作流创建方式 在git项目下,创建.github/workflows文件夹,里面编写的任何yml文件都是工作流的一部分。 secrets的创建方式 secrets.GITHUB_TOKEN是GitHub自动创建的,不需要自己创建。 secrets.SERVER_HOST这个的来源,需要在GitHub对应的项目的设置中设置。 readme的构建标签 配置好Branch、Event类型后,把下面这段内容复制到markdown文档里,就会自动显示自动化流程状态了。其实就是一张图片,由GitHub自动生成提供。 一. 自动构建网站并推送到远程服务器 自动构建hugo,并更新远程服务器的静态网站内容。 自动化流程文件 name: Deploy Hugo Site # 当推送到main分支时触发部署流程 on: push: branches: - main

By 夕元
代码版本管理工具(git/gerrit/repo)

代码版本管理工具(git/gerrit/repo)

原文链接:https://ovea-y.cn/code_version_control_tools__git_gerrit_repo/ 一、版本控制工具的历史 1.1 版本控制雏形 在版本控制软件出现之前,就具备diff与patch工具来对源码进行比较和打补丁了,在CVS出来的一段时间里,Linus一直在使用diff与patch工具管理着Linux的代码。diff与patch也是源码版本控制中最基本的概念。 1.1.1 diff —— 用于比较两个文件或目录之间的差异 -u 表示使用 unified 格式 -r 表示比较目录 -N 表示将不存在的文件当作空文件处理,这样新添加的文件也会出现在patch文件中 diff -urN a.c b.c > c.patch 1.1.2 patch —— 用于应用差异修改 通过patch可以将原始文件变成目标文件,

By 夕元