【Rust每周一知】Rust為什麼會有String和&str?!長文預警!
- 2020 年 3 月 6 日
- 筆記
本文是Amos博客文章「Working with strings in Rust」的翻譯。
原文地址:https://fasterthanli.me/blog/2020/working-with-strings-in-rust/
人們選擇Rust編程語言時總會遇到一個問題:為什麼會有兩種字符串類型?為什麼會出現String和&str?
Amos在其另一篇文章"declarative-memory-management"中部分回答了這個問題。但是在本文中又進行了一些實驗,看看是否可以為Rust的做法「辯護」。文章主要分為C和Rust兩大部分。
C語言部分:
- print程序示例
- UTF-8編碼
- print程序處理UTF-8編碼
- 傳遞字符串
C語言的print程序示例
讓我們從簡單C程序開始,打印參數。
// in `print.c` #include <stdio.h> // for printf int main(int argc, char **argv) { for (int i = 0; i < argc; i++) { char *arg = argv[i]; printf("%sn", arg); } return 0; }
$ gcc print.c -o print $ ./print "ready" "set" "go" ./print ready set go
好的!很簡單。程序使用的是標準的C11
主函數簽名,該簽名用int
定義參數個數(argc
,參數計數),和用char**
或char *[]
「字符串數組」定義參數(argv
,參數向量)。然後,使用printf
格式說明符%s
將每個參數打印為字符串,其後跟n
換行符。確實,它將每個參數打印在自己的行上。
在繼續之前,請確保我們對正在發生的事情有正確的了解。修改以上的程序,使用%p
格式說明符打印指針!
// in `print.c` int main(int argc, char **argv) { printf("argv = %pn", argv); // new! for (int i = 0; i < argc; i++) { char *arg = argv[i]; printf("argv[%d] = %pn", i, argv[i]); // new! printf("%sn", arg); } return 0; }
$ gcc print.c -o print $ ./print "ready" "set" "go" argv = 0x7ffcc35d84a8 argv[0] = 0x7ffcc35d9039 ./print argv[1] = 0x7ffcc35d9041 ready argv[2] = 0x7ffcc35d9047 set argv[3] = 0x7ffcc35d904b go
好的,argv
是一個地址數組,在這些地址上有字符串數據。像這樣:

printf
的%s
格式符怎麼知道什麼時候停止打印?因為它只獲得一個地址,而不是起始地址和結束地址,或者起始地址和長度。讓我們嘗試自己打印每個參數:
// in `print.c` #include <stdio.h> // printf int main(int argc, char **argv) { for (int i = 0; i < argc; i++) { char *arg = argv[i]; // we don't know where to stop, so let's just print 15 characters. for (int j = 0; j < 15; j++) { char character = arg[j]; // the %c specifier is for characters printf("%c", character); } printf("n"); } return 0; }
$ gcc print.c -o print $ ./print "ready" "set" "go" ./printreadys readysetgoCD setgoCDPATH=. goCDPATH=.:/ho
哦哦~我們的命令行參數相互「滲入」。讓我們嘗試將我們的程序通過管道xxd
傳輸到一個十六進制的轉儲程序中,以查看發生了什麼事:
$ # note: "-g 1" means "show groups of one byte", $ # xxd defaults to "-g 2". $ ./print "ready" "set" "go" | xxd -g 1 00000000: 2e 2f 70 72 69 6e 74 00 72 65 61 64 79 00 73 0a ./print.ready.s. 00000010: 72 65 61 64 79 00 73 65 74 00 67 6f 00 43 44 0a ready.set.go.CD. 00000020: 73 65 74 00 67 6f 00 43 44 50 41 54 48 3d 2e 0a set.go.CDPATH=.. 00000030: 67 6f 00 43 44 50 41 54 48 3d 2e 3a 2f 68 6f 0a go.CDPATH=.:/ho.
啊啊!它們確實彼此跟隨,但是兩者之間有一些區別:這是相同的輸出,用^^
進行注釋的位置是分隔符:
00000000: 2e 2f 70 72 69 6e 74 00 72 65 61 64 79 00 73 0a ./print.ready.s. . / p r i n t ^^ r e a d y ^^
似乎每個參數都由值0來終止。確實,C具有以null終止的字符串。因此,我們可以「修復」我們的打印程序:
#include <stdio.h> // printf int main(int argc, char **argv) { for (int i = 0; i < argc; i++) { char *arg = argv[i]; // note: the loop condition is gone, we just loop forever. // well, until a 'break' at least. for (int j = 0;; j++) { char character = arg[j]; // technically, we ought to use '