环穿白河水库赤壁丹霞四十里长嵯

从张家口市后城镇曹家窑村出发的环穿路线,全长15公里,爬升不到800米,最高海拔处有1300多米,前3公里爬升消耗很大,到第7公里开始下山,没有爬升,到第10公里开始是在山间公路走。

总的来说风景很好,别的地方很少见到如此的岩石,有点像美国的酋长岩,最高处俯视整个山下的时候很震撼,但手机镜头拍不出来,真的好像站在针尖上下面全是万丈深渊,有点能体会撒旦带耶稣上了高山将整个世界指给他看的感觉。

基本这类型的山平时爬这个已经够了,上次的三界碑也是属于同一类型,但就没有这么好的风景了。

强度还可以,但也需要一些体力,不是很困难,路也比较容易认,就是要3小时车程,并且缺点也是全程没有遮挡,夏天会很晒,另外土太大,风沙吹起来浑身是灰尘。

经验:换了徒步登山的鞋会感觉好很多,徒步鞋注意要稍微比平时的鞋大一点,走长路后脚会肿,因此需要更多空间,走到最后感觉小脚趾挤的很难受,跟袜子太紧也有些许关系。北方的山很多灌木都带刺,长袖长裤还是必要,这里的灌木没有上次三界碑那么的扎,还是感恩的。

rust编译为wasm32-wasi失败

问题和这个问题一样

https://stackoverflow.com/questions/74968490/the-wasm32-wasi-target-may-not-be-installed-while-it-is-installed

如果自行rustup add target wasm32-wasi 则显示已添加,如果自行cargo build --target wasm32-wasi 则会失败,说没有这个target。

原因是因为系统里安装了两套rust,先清除rust

rustup self uninstall
brew uninstall rust

再重新安装rust

curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

Node.js 18 更新不再需要 Nodemon

以前开发Node的时候,需要如Nodemon一类的工具启动,监听文件变化自动重新加载源码,现在Node.js 18自带了这一个功能,使用方法:

# 监听一切的入口文件相关模块更改和import的第三方module更改
node --watch server.js

# 指定监听路径,除外的module更改不会触发刷新 
# 只支持 Windows, MacOS
node --watch-path=./src --watch-path=./tests index.js

# 刷新时保持原有log输出
node --watch --watch-preserve-output server.js

2月23日墩台山脊三界碑穿越徒步

从天津蓟县石炮沟村开始,到北京平谷红石门村,共12公里,用时5小时多。路比较好认,沿途都有徒步组织留下的贴纸,而且都是比较明显的路,只有过了三界碑之后有一些小路,上面有很多带刺的植物很扎,不太舒服,其余的路没有难度(只有一处山脊很窄,下面就是深渊需要小心点)。我的脚踝较弱,容易崴脚,大腿筋也不舒服,沿途很多碎石路,脚踩不正,脚踝受力不均,而且容易滑,感恩带了登山杖,否则就很辛苦。可能只有初春适合来,不适合夏天来,沿途没有遮挡会非常晒,但是2月份来又没有什么可看的,植物的枯叶还有没掉光的,有些背阴的地方还有积雪。这里风景一般,加上今天天气不是很好,如果晴朗能见度高可能还好一点。沿途能看到一段长城,但是路线并不会去长城那边。

因为是穿越,所以回程车需要到红石门村接我们,村口的村委会有公共厕所。当时很想喝可乐,但问了一个村民说没有小卖铺。最后小狗长的很有趣🐶

六只脚轨迹

A useful video tutorial about how to setup environment for Vite + React + Typescript + Eslint + Prettier + Vitest and so on

Some bash commands and github link in this video:

npm i -D eslint
npx eslint --init

npx install-peerdeps --dev eslint-config-airbnb
npm install eslint-config-airbnb-typescript
npm i -D prettier eslint-config-prettier eslint-plugin-prettier
# crete cjs file for prettier
npm i -D vitest
npm i -D @testing-library/react @testing-library/jest-dom

https://github.com/CodingGarden/react-ts-starter

近期发现一些值得收藏的网站

这类的网站太多,实在没有机会完整的看,但里面确实有些很好的内容,等到要找的时候找不到又非常可惜,深感个人的收藏夹里需要整理,但又没有找到很好的整理方法,放入收藏夹,肯定过一段时间也是找不到了,希望能够有更好的方式管理这些东西,先存在这看以后还会不会用到吧。

一个介绍各种算法和数据结构在JS里实现的仓库,很有价值:https://github.com/trekhleb/javascript-algorithms

一个自学类网站,可以交互式学习计算机科学课程:https://github.com/freeCodeCamp/freeCodeCamp

开发类面试大全:https://github.com/jwasham/coding-interview-university

各种各样的技术工具,不管有没有用先收起来:https://github.com/trimstray/the-book-of-secret-knowledge#other-cheat-sheets-toc

数据建模和数据分析的平台:https://www.kaggle.com/

用Tauri+React构建桌面应用

Tauri是一个取代Electron的方案,而且比Electron应用方面更广,马上也可以用来创建手机应用,而且架构使用rust作为后端,不嵌入chromium的方式也大大减小app的体积和占用的内存。

简单用一个实例说明如何使用react项目构建一个Tauri App,只需要很短的时间就可以完成此教程。

预先安装

需要先安装好npm、cargo等运行环境,参考 https://tauri.app/zh-cn/v1/guides/getting-started/prerequisites

安装tauri-cli

cargo install tauri-cli

创建react项目

如果已有react项目可以略过这一步

npx create-react-app hello-tauri-react

创建Tauri app

cd hello-tauri-react # 进入react项目的路径中

cargo tauri init

这里会问你6个问题,作为配置写在生成好的tauri.conf.json文件里,结束后会为你创建 src-tauri文件夹

第五个问题问你如何启动前端环境,因为是用CRA创建的项目,因此应该是 npm start

其余问题保持原样就可以

启动测试环境

cargo tauri dev

这里它会先启动react环境,再启动app客户端,当你看到这个界面,就是成功了

客户端界面

你会注意到,浏览器窗口自动弹起来是不必要的,打开tauri.conf.json文件,修改 build -> beforeDevCommand

"beforeDevCommand": "BROWSER=none npm start",

这样下次启动的时候就不会再打开浏览器窗口了

发布打包

cargo tauri build

会报一个错误

`com.tauri.dev` is not allowed as it must be unique across application

就是说app的id标识符不让你用com.tauri.dev这个名字,在tauri.conf.json 中找到 tauri -> bundle -> identifier 随便改成你自己想取的名字,再运行

cargo tauri build

Bingo!现在应该可以发布的app生成好了,我是mac系统,文件生成在 src-tauri/target/release/bundle/ 中 pkg是打包后的,macos里有可运行的app,它还帮你生成了安装包,真的是很愉快的开发体验!

Rust总结:可恢复的错误Result

可恢复的错误 Result

unwrap/expect 如果失败就panic

use std::fs::File;

fn main() {
    // 如果调用这段代码时 hello.txt 文件不存在,那么 unwrap 就将直接 panic
    let f = File::open("hello.txt").unwrap();
}

? 和报错传播

fn open_file() -> Result<File, Box<dyn std::error::Error>> {
    // 如果失败就自动return Err
    let mut f = File::open("hello.txt")?;
    Ok(f)
}

? 不仅仅可以用于 Result 的传播,还能用于 Option 的传播

fn last_char_of_first_line(text: &str) -> Option<char> {
    text.lines().next()?.chars().last()
}

新手用 ? 常会犯的错误

初学者在用 ? 时,老是会犯错,例如写出这样的代码:

fn first(arr: &[i32]) -> Option<&i32> {
   arr.get(0)?
}

切记:? 操作符需要一个变量来承载正确的值,这个函数只会返回 Some(&i32) 或者 None,然而只有错误值能直接返回,正确的值不行

实际上 Rust 还支持另外一种形式的 main 函数

use std::error::Error;
use std::fs::File;

fn main() -> Result<(), Box<dyn Error>> {
    let f = File::open("hello.txt")?;

    Ok(())
}

例子

1

use std::num::ParseIntError;

fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
    let n1 = n1_str.parse::<i32>();
    let n2 = n2_str.parse::<i32>();
    Ok(n1.unwrap() * n2.unwrap())
}

fn main() {
    let result = multiply("10", "2");
    assert_eq!(result, Ok(20));

    let result = multiply("4", "2");
    assert_eq!(result.unwrap(), 8);

    println!("Success!")
}

? 改写

use std::num::ParseIntError;

// IMPLEMENT multiply with ?
// DON'T use unwrap here
fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
    let n1 = n1_str.parse::<i32>()?;
    let n2 = n2_str.parse::<i32>()?;
    Ok(n1 * n2)
}

fn main() {
    assert_eq!(multiply("3", "4").unwrap(), 12);
    println!("Success!")
}

连续?

use std::fs::File;
use std::io::{self, Read};

fn read_file1() -> Result<String, io::Error> {
    let f = File::open("hello.txt");
    let mut f = match f {
        Ok(file) => file,
        Err(e) => return Err(e),
    };

    let mut s = String::new();
    match f.read_to_string(&mut s) {
        Ok(_) => Ok(s),
        Err(e) => Err(e),
    }
}

fn read_file2() -> Result<String, io::Error> {
    let mut s = String::new();
    // 注意 read_to_string(&mut s)?的?虽然不写也可以通过,但是就错过了可能报的错
    File::open("hello.txt")?.read_to_string(&mut s)?;

    Ok(s)
}

fn main() {
    assert_eq!(read_file1().unwrap_err().to_string(), read_file2().unwrap_err().to_string());
    println!("Success!")
}

mapand_then的使用

use std::num::ParseIntError;

fn add_two(n_str: &str) -> Result<i32, ParseIntError> {
   n_str.parse::<i32>().map(|num| num +2)
}

fn main() {
    assert_eq!(add_two("4").unwrap(), 6);

    println!("Success!")
}
use std::num::ParseIntError;

fn add_two(n_str: &str) -> Result<i32, ParseIntError> {
   n_str.parse::<i32>().and_then(|num| Ok(num +2))
}

fn main() {
    assert_eq!(add_two("4").unwrap(), 6);

    println!("Success!")
}

map/and_then改写

use std::num::ParseIntError;

// With the return type rewritten, we use pattern matching without `unwrap()`.
// But it's so Verbose..
fn multiply(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
    match n1_str.parse::<i32>() {
        Ok(n1)  => {
            match n2_str.parse::<i32>() {
                Ok(n2)  => {
                    Ok(n1 * n2)
                },
                Err(e) => Err(e),
            }
        },
        Err(e) => Err(e),
    }
}

// Rewriting `multiply` to make it succinct
// You  MUST USING `and_then` and `map` here
fn multiply1(n1_str: &str, n2_str: &str) -> Result<i32, ParseIntError> {
    // IMPLEMENT...
    n1_str.parse::<i32>().and_then(|n1| {
        n2_str.parse::<i32>().map(|n2| n1 * n2)
    })
}

fn print(result: Result<i32, ParseIntError>) {
    match result {
        Ok(n)  => println!("n is {}", n),
        Err(e) => println!("Error: {}", e),
    }
}

fn main() {
    // This still presents a reasonable answer.
    let twenty = multiply1("10", "2");
    print(twenty);

    // The following now provides a much more helpful error message.
    let tt = multiply("t", "2");
    print(tt);

    println!("Success!")
}

简化类型

use std::num::ParseIntError;

// Define a generic alias for a `Result` with the error type `ParseIntError`.
type Res<T> = Result<T, ParseIntError>;

// Use the above alias to refer to our specific `Result` type.
fn multiply(first_number_str: &str, second_number_str: &str) -> Res<i32> {
    first_number_str.parse::<i32>().and_then(|first_number| {
        second_number_str.parse::<i32>().map(|second_number| first_number * second_number)
    })
}

// Here, the alias again allows us to save some space.
fn print(result: Res<i32>) {
    match result {
        Ok(n)  => println!("n is {}", n),
        Err(e) => println!("Error: {}", e),
    }
}

fn main() {
    print(multiply("10", "2"));
    print(multiply("t", "2"));
}

main里的return问题

原题如果改成用特征对象就会报错

use std::num::ParseIntError;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let number_str = "10";
    let number = match number_str.parse::<i32>() {
        Ok(number)  => number,
        Err(e) => return Err(e),
    };
    println!("{}", number);
    Ok(())
}

?就成功,这是为什么呢?不是说?是个宏,其实也是return了Err(e)的吗?

use std::num::ParseIntError;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let number_str = "10";
    let number = number_str.parse::<i32>()?;
    println!("{}", number);
    Ok(())
}