0%

## rust学习笔记-所有权三之切片

Posted on 2021-05-16 In 语言 , Rust Views: Disqus:

除了引用，Rust 还有另外一种不持有所有权的数据类型：切片(slice)。切片允许我们引用集合中某一段连续的元素序列，而不是整个集合。
例如代码

    fn main() {
        let mut s = String::from("hello world");

        let word = first_word(&s);

        s.clear();

        // 这时候虽然 word 还是 5，但是 s 已经被清除了，所以就没存在的意义
    }

这里其实我们就需要关注 s 的存在性，代码的逻辑合理性就需要额外去维护，此时我们就可以用切片

    let s = String::from("hello world")

    let hello = &s[0..5];
    let world = &s[6..11];

其实跟 Python 的list 之类的语法有点类似，当然里面还有些语法糖，比如可以直接用省略后面的数字表示直接引用到结尾

    let hello = &s[0..];

甚至再进一步

    let hello = &s[..];

使用了切片之后

    fn first_word(s: &String) -> &str {
        let bytes = s.as_bytes();

        for (i, &item) in bytes.iter().enumerate() {
            if item == b' ' {
                return &s[0..i];
            }
        }

        &s[..]
    }
    fn main() {
        let mut s = String::from("hello world");

        let word = first_word(&s);

        s.clear(); // error!

        println!("the first word is: {}", word);
    }

那再执行 main 函数的时候就会抛错，因为 word 还是个切片，需要保证 s 的有效性，并且其实我们可以将函数申明成

    fn first_word(s: &str) -> &str {

这样就既能处理&String 的情况，就是当成完整字符串的切片，也能处理普通的切片。
其他类型的切片

    let a = [1, 2, 3, 4, 5];
    let slice = &a[1..3];

简单记录下，具体可以去看看这本书

## 聊聊给亲戚朋友的老电脑重装系统那些事儿

Posted on 2021-05-09 In 生活 Views: Disqus:

前面这个五一回去之前，LD 姐姐跟我说电脑很卡了，想让我重装系统，问了下 LD 可能是那个 09 年买的笔记本，想想有点害怕[捂脸]，前年有一次好像让我帮忙装了她同事的一个三星的笔记本，本着一些系统洁癖，所以就从开始找纯净版的 win7 家庭版，因为之前那些本基本都自带 win7 的家庭版，而且把激活码就贴在机器下面，然后从三星官网去找官方驱动，还好这个机型的驱动还在，先做了系统镜像，其实感觉这种情况需要两个 U 盘，一个 U 盘装系统作为安装启动盘，一个放驱动，毕竟不是专业装系统的，然后因为官方驱动需要一个个下载一个个安装，然后驱动文件下载的地方还没标明是 32 位还是 64 位的，结果还被 LD 姐姐催着，一直问好没好，略尴尬，索性还是找个一键安装的

这次甚至更夸张，上次还让带回去，我准备好了系统镜像啥的，第二天装，这次直接带了两个老旧笔记本过来说让当天就装好，感觉有点像被当修电脑的使，又说这些电脑其实都不用了的，都是为了她们当医生的要每年看会课，然后只能用电脑浏览器看，结果都在用 360 浏览器，真的是万恶的 360，其实以前对 360 没啥坏印象，毕竟以前也经常用，只是对于这些老电脑，360 全家桶真的就是装了就废了，2G 的内存，开机就开着 360 安全卫士，360 杀毒，有一个还装了腾讯电脑管家，然后腾讯视频跟爱奇艺也开机启动了，然后还打开 360 浏览器看课，就算再好的系统也吃不消这么用，重装了系统，还是这么装这些东西，也是分分钟变卡，可惜他们都没啥这类概念。

对于他们要看的课，更搞笑的是，明明在页面上注明了说要使用 IE 浏览器，结果他们都在用 360 浏览器看，但是这个也不能完全怪他们，因为实在是现在的 IE 啥的也有开始不兼容 flash 的配置，需要开启兼容配置，但是只要开启了之后就可以直接用 IE 看，比 360 靠谱很多， 资源占用也比较少，360 估计是基于 chromium 加了很多内置的插件，本身 chromium 也是内存大户，但是说这些其实他们也不懂，总觉得找我免费装下系统能撑一段时间，反正对我来说也应该很简单（他们觉得），实际上开始工作以后，我自己想装个双系统都是上淘宝买别人的服务装的，台式机更是几年没动过系统了，因为要重装一大堆软件，数据备份啥的，还有驱动什么的，分区格式，那些驱动精灵啥的也都是越来越坑，一装就给你带一堆垃圾软件。

感悟是，总觉得学计算机的就应该会装系统，会修电脑，之前亲戚还拿着一个完全开不起来的笔记本让我来修，这真的是，我说可以找官方维修的，结果我说我搞不定，她直接觉得是修不好了，直接电脑都懒得拿回去了，后面又一次反复解释了才明白，另外就是 360 全家桶，别说老电脑了，新机器都不太吃得消。

## Leetcode 48 旋转图像(Rotate Image) 题解分析

Posted on 2021-05-01 In Java , leetcode , Rotate Image Views: Disqus:

### 题目介绍

You are given an n x n 2D `matrix` representing an image, rotate the image by 90 degrees (clockwise).

You have to rotate the image in-place, which means you have to modify the input 2D matrix directly. DO NOT allocate another 2D matrix and do the rotation.
如图，这道题以前做过，其实一看有点蒙，好像规则很容易描述，但是代码很难写，因为要类似于贪吃蛇那样，后来想着应该会有一些特殊的技巧，比如翻转等

### 代码

直接上码

    public void rotate(int[][] matrix) {
            // 这里真的傻了，长宽应该是一致的，所以取一次就够了
            int lengthX = matrix[0].length;
            int lengthY = matrix.length;
            int temp;
            System.out.println(lengthY - (lengthY % 2) / 2);
            // 这里除错了，应该是减掉余数再除 2
    //        for (int i = 0; i < lengthY - (lengthY % 2) / 2; i++) {
            /**
             * 1 2 3             7 8 9
             * 4 5 6     =>      4 5 6     先沿着 4 5 6 上下交换
             * 7 8 9             1 2 3
             */
            for (int i = 0; i < (lengthY - (lengthY % 2)) / 2; i++) {
                for (int j = 0; j < lengthX; j++) {
                    temp = matrix[i][j];
                    matrix[i][j] = matrix[lengthY-i-1][j];
                    matrix[lengthY-i-1][j] = temp;
                }
            }

            /**
             * 7 8 9               7 4 1
             * 4 5 6     =>        8 5 2   这里再沿着 7 5 3 这条对角线交换
             * 1 2 3               9 6 3
             */
            for (int i = 0; i < lengthX; i++) {
                for (int j = 0; j <= i; j++) {
                    if (i == j) {
                        continue;
                    }
                    temp = matrix[i][j];
                    matrix[i][j] = matrix[j][i];
                    matrix[j][i] = temp;
                }
            }
        }

还没到可以直接归纳题目类型的水平，主要是几年前做过，可能有那么点模糊的记忆，当然应该也有直接转的方法

## rust学习笔记-所有权一

Posted on 2021-04-18 In 语言 , Rust Views: Disqus:

最近在看 《rust 权威指南》，还是难度比较大的，它里面的一些概念跟之前的用过的都有比较大的差别
比起有 gc 的虚拟机语言，跟像 C 和 C++这种主动释放内存的，rust 有他的独特点，主要是有三条

- Rust中的每一个值都有一个对应的变量作为它的所有者。
- 在同一时间内，值有且只有一个所有者。
- 当所有者离开自己的作用域时，它持有的值就会被释放掉。
  这里有两个重点：
- s 在进入作用域后才变得有效
- 它会保持自己的有效性直到自己离开作用域为止

然后看个案例

    let x = 5;
    let y = x;

这个其实有两种，一般可以认为比较多实现的会使用 copy on write 之类的，先让两个都指向同一个快 5 的存储，在发生变更后开始正式拷贝，但是涉及到内存处理的便利性，对于这类简单类型，可以直接拷贝
但是对于非基础类型

    let s1 = String::from("hello");
    let s2 = s1;

    println!("{}, world!", s1);

有可能认为有两种内存分布可能
先看下 string 的内存结构
第一种可能是
第二种是
我们来尝试编译下
发现有这个错误，其实在 rust 中`let y = x`这个行为的实质是移动，在赋值给 y 之后 x 就无效了
这样子就不会造成脱离作用域时，对同一块内存区域的二次释放，如果需要复制，可以使用 clone 方法

    let s1 = String::from("hello");
    let s2 = s1.clone();

    println!("s1 = {}, s2 = {}", s1, s2);

这里其实会有点疑惑，为什么前面的`x, y` 的行为跟 `s1, s2` 的不一样，其实主要是基本类型和 string 这类的不定大小的类型的内存分配方式不同，`x, y`这类整型可以直接确定大小，可以直接在栈上分配，而像 string 和其他的变体结构体，其大小都是不能在编译时确定，所以需要在堆上进行分配

## rust学习笔记-所有权二

Posted on 2021-04-18 In 语言 , Rust Views: Disqus:

这里需要说道函数和返回值了
可以看书上的这个例子
对于这种情况，当进入函数内部时，会把传入的变量的所有权转移进函数内部，如果最后还是要返回该变量，但是如果此时还要返回别的计算结果，就可能需要笨拙地使用元组

### 引用

此时我们就可以用引用来解决这个问题

    fn main() {
        let s1 = String::from("hello");
        let len = calculate_length(&s1);

        println!("The length of '{}' is {}", s1, len);
    }
    fn calculate_length(s: &String) -> usize {
        s.len()
    }

这里的&符号就是引用的语义，它们允许你在不获得所有权的前提下使用值
由于引用不持有值的所有权，所以当引用离开当前作用域时，它指向的值也不会被丢弃

### 可变引用

而当我们尝试对引用的字符串进行修改时

    fn main() {
        let s1 = String::from("hello");
        change(&s1);
    }
    fn change(s: &String) {
        s.push_str(", world");
    }

就会有以下报错，
其实也很容易发现，毕竟没有 mut 指出这是可变引用，同时需要将 s1 改成 mut 可变的

    fn main() {
        let mut s1 = String::from("hello");
        change(&mut s1);
    }

    fn change(s: &mut String) {
        s.push_str(", world");
    }

再看一个例子

    fn main() {
        let mut s1 = String::from("hello");
        let r1 = &mut s1;
        let r2 = &mut s1;
    }

这个例子在书里是会报错的，因为同时存在一个以上的可变引用，但是在我运行的版本里前面这段没有报错，只有当我真的要去更改的时候

    fn main() {
        let mut s1 = String::from("hello");
        let mut r1 = &mut s1;
        let mut r2 = &mut s1;
        change(&mut r1);
        change(&mut r2);
    }

    fn change(s: &mut String) {
        s.push_str(", world");
    }

这里可能就是具体版本在实现上的一个差异，我用的 rustc 是 1.44.0 版本
其实上面的主要是由 rust 想要避免这类多重可变更导致的异常问题，总结下就是三个点

- 两个或两个以上的指针同时同时访问同一空间
- 其中至少有一个指针会想空间中写入数据
- 没有同步数据访问的机制
  并且我们不能在拥有不可变引用的情况下创建可变引用

  ### 悬垂引用

  还有一点需要注意的就是悬垂引用

      fn main() {
          let reference_to_nothing = dangle();
      }

      fn dangle() -> &String {
          let s = String::from("hello");
          &s
      }

  这里可以看到其实在 dangle函数返回后，这里的 s 理论上就离开了作用域，但是由于返回了 s 的引用，在 main 函数中就会拿着这个引用，就会出现如下错误

  ### 总结

  最后总结下
- 在任何一个段给定的时间里，你要么只能拥有一个可变引用，要么只能拥有任意数量的不可变引用。
- 引用总是有效的。