hello rust

rustは全部入り

goに行くかrustに行くか迷ったけども、rustを選択。何故かと言うとGCが無いから。 混ぜればごみ、分れば資源って合言葉を書いたごみ回収車が走っている。これにインスパイアーされたのさ。

GCはLISPの発明品。メモリーを裏で回収して、メモリーが無限にあるような幻想をいだかしている。これって、消費者を欺いていないか? 冷凍食品が売れる、コンビニ便利、、、なのはいいけど、大量にごみを撒き散らしている。反省しなさいは、コンピュータの世界も一緒。

大体、使い放題だから、辛抱強いおかーさんが、裏でメモリーを回収しまくっているんだ。その時は、世界が止められてしまう。そんな生活はいやだ。塵回収が必要になるのは、資源をジャブジャブ使い放題に使っているから。ごみの出ない、ださない生活をすれば一挙に問題解決。

さあ、ごみが出ない新生活へスイッチしよう(そういうなら、今後一切schemeなんて使うなと言われそう、ついでにrubyとかもだぞ)。

少し、人様の主張を聞いてみるか。

関数型プログラマからみたRust

プログラミング言語Rustのススメ

なぜRustを学ぶべきなのか 〜 5年経った今改めてまとめてみる

HaskellerがRustに入門してみた

Re:FizzBuzzから始めるRust生活 これはお勧めな記事だ。

Rustに影響を与えた言語たち 良い所だけ取ってきて言語を作りましたとな。

そして、良く使うであろう Primitive Type str とか、人様のノウハウを集めた Rustで初めてツールを作るときに役にたったリンク集 が、参考になろう。

emacs + rust

例によってemacsからrustと言うかcargoを操作出来るようにしておく。世間一般にはVSCODEって言うMS製のEDITORを使うのが鉄板らしいけど、ロートルはCUIが好きなのよ。

GUIな編集装置向けにrustもサービスに一生懸命。その現れがrust-nalyzerって言うコンパイラーのフロントエンド。こいつの力を借りて、補完とかをする。で、ダイレクトにEDITORとやり取りする訳ではなく、間にインターフェースを介して行う。それがemacs上では、lsp-mode. MSの陰謀で提案されたものが、emacs用に移植されてるって訳。

rustは、産地直送で、ダイレクトにユーザーに屆くようになってる。間にOSなんていう問屋が有ると、どうしてもフレッシュな最新阪を届けられないからね。一度ご利用頂くと、その後はrustup upgradeってコマンド一発で、最新版に更新出来る。対応は、Windows,MacOS,Linux限定。

もうBSDはすっかり蚊帳の外だ。ついでに、32ビットマシンも対象外。そこでだ、

manualとか Rustプログラミングのための環境構築 を参照しつつ、手軽に Rust開発環境 on Emacs更新 に従って、rustic,rust-analyzerを入れる。

OpenBSDにソースからrust-analyzerを入れようとしたら、年式が2018なんで駄目だと言われたよ。2021式が來るのは何時だ?

しょうがないので、補完とかを諦め、その代わりにrust-gdbを入れておいた。こいつはどうしても欲しいからね。こやつshell-scriptなのね。核心部分は

# Find out where the pretty printer Python module is
RUSTC_SYSROOT="$("$RUSTC" --print=sysroot)"
GDB_PYTHON_MODULE_DIRECTORY="$RUSTC_SYSROOT/lib/rustlib/etc"

# Run GDB with the additional arguments that load the pretty printers
# Set the environment variable `RUST_GDB` to overwrite the call to a
# different/specific command (defaults to `gdb`).
RUST_GDB="${RUST_GDB:-egdb}"
PYTHONPATH="$PYTHONPATH:$GDB_PYTHON_MODULE_DIRECTORY" exec ${RUST_GDB} \
  --directory="$GDB_PYTHON_MODULE_DIRECTORY" \
  -iex "add-auto-load-safe-path $GDB_PYTHON_MODULE_DIRECTORY" \
  "$@"

pythonを使って、綺麗に見せますって方針になってる。

New year

年末に、友人から、もー幾つ寝るとお正月なんていう楽しいメールを頂いていた。それの返答がこれ。新年の挨拶用にも転用。久し振りにかくコードだ。構造体の名前が型名ってのが、何実にCより進歩してんな。

fn main() {
    struct Year {
        y: i16,
        d: i16,
    }
    let mut yd = Year { y: 2021, d: 365 };
    while yd.d > 0 {
        yd.d -= 1
    }
    println!("{} New year!", yd.y + 1);
}

rustには、–i,++iという便利なやつが無いのは、確信犯なのかな? 何故だろう。

今年初めてのつたない英語、why don't support increment operator in rust で聞いてみた。出て来た案内を数Hopしたら、https://github.com/dtolnay/rust-faq#why-doesnt-rust-have-increment-and-decrement-operators 正しい言い回しに辿りついた。

公式回答では、++,– は、複雑であいまいさがありBUGの温床になるから、サポートしないよって箏だ。他のFAQもなかなか面白いな。

折角なのでバグを仕込んでみる。日々を足すね。

vbox$ cargo r
    Finished dev [unoptimized + debuginfo] target(s) in 0.04s
     Running `target/debug/hoge`
thread 'main' panicked at 'attempt to add with overflow', src/main.rs:9:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

オーバーフローを掴まえてくれた。詳しく見てみる。あれ、省略されちゃってるよ。

OpenBSDと同じ年式のFreeBSDでもやってみる。

[sakae@fb /tmp/hoge]$ rustc -V
rustc 1.55.0
[sakae@fb /tmp/hoge]$ head Cargo.toml
[package]
name = "hoge"
version = "0.1.0"
edition = "2018"

バージョン番号とeditionと言う年式の2本立てになってる。年式が同じだと共通仕様って事で、多分問題なく動くんだろうね。で、属に言う Run Time Errorのおでまし。

[sakae@fb /tmp/hoge]$ RUST_BACKTRACE=1 cargo r
    Finished dev [unoptimized + debuginfo] target(s) in 0.03s
     Running `target/debug/hoge`
thread 'main' panicked at 'attempt to add with overflow', src/main.rs:8:9
stack backtrace:
   0: rust_begin_unwind
   1: core::panicking::panic_fmt
   2: core::panicking::panic
   3: hoge::main
             at ./src/main.rs:8:9
   4: core::ops::function::FnOnce::call_once
             at /wrkdirs/usr/ports/lang/rust/work/rustc-1.55.0-src/library/core/src/ops/function.rs:227:5
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

full traceも出来るってんで、上記の上流も出て来た。しっかりrustを作成した時の痕跡が記憶されてるな。

:
13:  0x1010791 - std::sys_common::backtrace::__rust_begin_short_backtrace::h81de2ce2115f8265
                     at /wrkdirs/usr/ports/lang/rust/work/rustc-1.55.0-src/library/std/src/sys_common/backtrace.rs:125:18
14:  0x1010b33 - std::rt::lang_start::{{closure}}::h7edc9139a1dc248d
                     at /wrkdirs/usr/ports/lang/rust/work/rustc-1.55.0-src/library/std/src/rt.rs:63:18
15:  0x10267b9 - std::rt::lang_start_internal::h6521f84e51a30755
16:  0x1010af0 - std::rt::lang_start::ha93e0e8c16851be6
                     at /wrkdirs/usr/ports/lang/rust/work/rustc-1.55.0-src/library/std/src/rt.rs:62:5
17:  0x1010927 - main

manでリリース版のやり方を調べて実行。あれ? BUGが潰れているぞ。

[sakae@fb /tmp/hoge]$ man cargo-run ;; cargo help run for linux
[sakae@fb /tmp/hoge]$ cargo r  --release
    Finished release [optimized] target(s) in 0.02s
     Running `target/release/hoge`
2022 New year!

信じられない挙動だ。ええい、直の実行。

[sakae@fb /tmp/hoge]$ target/release/hoge
2022 New year!
[sakae@fb /tmp/hoge]$ target/debug/hoge
thread 'main' panicked at 'attempt to add with overflow', src/main.rs:8:9
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

こいつぁ、冬の珍事? かな。cargo cleanして、綺麗に痕跡を消してから実行しても、珍事は続くよ。正気のさたか???

こうなったら、河岸を変えて2021年式でcargoしてみる。いわゆる本家本元ね。 その前に、ちゃんとHELPを読め。 https://doc.rust-lang.org/cargo/reference/profiles.html

[profile.release]
opt-level = 3
debug = false
split-debuginfo = '...'  # Platform-specific.
debug-assertions = false
overflow-checks = false
lto = false
panic = 'unwind'
incremental = false
codegen-units = 16
rpath = false

こいつに引掛ったんだな。詐欺っぽいぞ。通報するぞ、って、既に公式になってるじゃん。

exec …

tokioで外部コマンド実行 なんてのを見付けた。けど、究極はshellだよね。歳の始めに見付けておいた自作shellを試してみる。

vbox$ cargo b
error: failed to parse manifest at `/tmp/shell-sample-code/step1/Cargo.toml`

Caused by:
  feature `edition2021` is required

  The package requires the Cargo feature called `edition2021`, but that feature is not stabilized in this version of Cargo (1.55.0).
  Consider trying a newer version of Cargo (this may require the nightly release).
  See https://doc.rust-lang.org/nightly/cargo/reference/unstable.html#edition-2021 for more information about the status of this feature.

お前の持ってるやつは、さび ついているから、新しいrustにしろって言われた。そうよ、pkgからだと陳腐なものしか用意出来無いのよ。まあ、諦めて年式が古いやつですって設定した。

vbox$ time cargo b
   Compiling autocfg v1.0.1
   Compiling libc v0.2.112
   Compiling bitflags v1.3.2
   Compiling cfg-if v1.0.0
   Compiling memoffset v0.6.5
   Compiling nix v0.23.1
   Compiling toysh v0.1.0 (/tmp/shell-sample-code/step1)
    Finished dev [unoptimized + debuginfo] target(s) in 24.64s
    0m24.68s real     0m24.46s user     0m08.67s system

外部の依存は極力少くなるように配慮されてるようだけど、それでも関連品を含めてこれだけコンパイルされた。もう宿命なんだろうね。

vbox$ cargo r
    Finished dev [unoptimized + debuginfo] target(s) in 0.07s
     Running `target/debug/toysh`
> uname -a
OpenBSD vbox.local.jp 7.0 GENERIC.MP#3 i386

リナ用って事だけど、普通に動いた。へんてこなシステムコールをしない限りは大丈夫そう。

続いてgdbにかけてみる。emacsからだとrust-gdbが動かないので(なんたって、上で見たように、ラッパーですから)、別端末でアプリを起動して、それにアタッチした。

vbox$ rust-gdb -q toysh -p 54129
Reading symbols from toysh...done.
Attaching to program: /tmp/shell-sample-code/step1/target/debug/toysh, process 54129
Reading symbols from /usr/lib/libc++abi.so.5.0...done.
Reading symbols from /usr/lib/libc.so.96.1...done.
Reading symbols from /usr/libexec/ld.so...done.
[Switching to thread 200238]
_thread_sys_read () at /tmp/-:3
3       /tmp/-: No such file or directory.
(gdb) b execve
Breakpoint 1 at 0xf90c100: file /tmp/-, line 3.

そして、システムコールの呼出にBPを億。別端末のアプリに入力。

Breakpoint 1, execve () at /tmp/-:3
3       /tmp/-: No such file or directory.
(gdb) bt
#0  execve () at /tmp/-:3
#1  0x0f9817d0 in _libc_execvpe (name=0x560e0230 "uname", argv=0x560bfd70,
    envp=0xcf7e139c) at /usr/src/lib/libc/gen/exec.c:201
#2  0x0f981577 in _libc_execvp (name=0x560e0230 "uname", argv=0x560bfd70)
    at /usr/src/lib/libc/gen/exec.c:249
#3  0x19febab9 in nix::unistd::execvp::hde14bd861e4012af (filename=0x560e0230,
    args=&[std::ffi::c_str::CString](size=2) = {...})
    at /home/sakae/.cargo/registry/src/github.com-1285ae84e5963aae/nix-0.23.1/src/unistd.rs:781
#4  0x19feeeac in toysh::shell_exec_simple_command::he199460e396cd07e (
    command=...) at src/main.rs:70
#5  0x19fee8a1 in toysh::shell_loop::h3b20613e6bb4af99 () at src/main.rs:24
#6  0x19fee757 in toysh::main::h4d8f867332179c54 () at src/main.rs:13
#7  0x19fe7958 in core::ops::function::FnOnce::call_once::h1db125a82d9e4cf6 ()
    at /pobj/rust-1.55.0/rustc-1.55.0-src/library/core/src/ops/function.rs:227
#8  0x19fec123 in std::sys_common::backtrace::__rust_begin_short_backtrace::ha7f
35dc1e7ee0e86 (f=0x19fee740 <toysh::main::h4d8f867332179c54>)
    at /pobj/rust-1.55.0/rustc-1.55.0-src/library/std/src/sys_common/backtrace.rs:125
#9  0x19feb2b5 in std::rt::lang_start::_$u7b$$u7b$closure$u7d$$u7d$::h8901638a45
779c16 () at /pobj/rust-1.55.0/rustc-1.55.0-src/library/std/src/rt.rs:63
#10 0x1a003f02 in std::rt::lang_start_internal::hfe0abd7a78dfa937 ()
#11 0x19feb263 in std::rt::lang_start::h3cd98e76ceb84846 (
    main=0x19fee740 <toysh::main::h4d8f867332179c54>, argc=1, argv=0xcf7e1394)
    at /pobj/rust-1.55.0/rustc-1.55.0-src/library/std/src/rt.rs:62
#12 0x19feef3b in main ()

結構深い実行履歴が出てきたな。

(gdb) f 3
#3  0x19febab9 in nix::unistd::execvp::hde14bd861e4012af (filename=0x560e0230,
    args=&[std::ffi::c_str::CString](size=2) = {...})
    at /home/sakae/.cargo/registry/src/github.com-1285ae84e5963aae/nix-0.23.1/sr
c/unistd.rs:781
781             libc::execvp(filename.as_ptr(), args_p.as_ptr())
(gdb) l
776     #[inline]
777     pub fn execvp<S: AsRef<CStr>>(filename: &CStr, args: &[S]) -> Result<Inf
allible> {
778         let args_p = to_exec_array(args);
779
780         unsafe {
781             libc::execvp(filename.as_ptr(), args_p.as_ptr())
782         };
783
784         Err(Errno::last())
785     }

外部ライブラリィーの活躍の場を視察。インラインになってるって事は、スピード重視なんだな。

cすると何度かBPにヒットし、最後はSIGTRAP。この時、別画面で起動してたコマンドが実行されて、結果が出てきた。何度も隠れてexecveが走っているのね。

cargo

cargoはRustシステムのshellです。newとbuildとrunだけじゃ、もったいない。ってんで、オイラーの琴線に触れたものをあげておく。

-j

cargoはmakeも兼ているんで、きっとあれが有るはず。

sakae@pen:/tmp/shell-sample-code/step1$ time cargo b
  :
real    0m13.154s
user    0m11.751s
sys     0m9.161s

並行コンパイルON

sakae@pen:/tmp/shell-sample-code/step1$ time cargo b -j 4
  :
real    0m9.069s
user    0m12.272s
sys     0m3.101s

多少はイライラが減少するかな。

tree

ライブラリィってかCratesの、こんがらがり具合を確認するやつ

sakae@pen:/tmp/shell-sample-code/step1$ cargo tree
toysh v0.1.0 (/tmp/shell-sample-code/step1)
└── nix v0.23.1
    ├── bitflags v1.3.2
    ├── cfg-if v1.0.0
    ├── libc v0.2.112
    └── memoffset v0.6.5
        [build-dependencies]
        └── autocfg v1.0.1

このコマンド、cargo –helpでも案内に出てこないんだ。余り使われると、Cratesの作者様の手抜き具合、もとえ、人の褌で相撲をとる具合、もとえ、依存具合が白昼の元に晒されるんで、隠してるのさ。

doc –open

ドキュメントをソースから自動生成して、それをブラウザーに引き継いで閲覧出来る。

sakae@pen:/tmp/shell-sample-code/step1$ cargo doc --open
    Checking bitflags v1.3.2
    Checking cfg-if v1.0.0
 Documenting bitflags v1.3.2
 Documenting cfg-if v1.0.0
  :
 Documenting toysh v0.1.0 (/tmp/shell-sample-code/step1)
    Finished dev [unoptimized + debuginfo] target(s) in 8.59s
     Opening /tmp/shell-sample-code/step1/target/doc/toysh/index.html

出来上がったhtml類は、target/docの中にある。直接見るものではない。ブラウザーを通して、関連のCratesも参照出来るぞ。

例えば、libcのfunctionの項目にあるexecve

pub unsafe extern "C" fn execve(
    prog: *const c_char, 
    argv: *const *const c_char, 
    envp: *const *const c_char
) -> c_int

いちいち探し回らなくても、検索窓が有るから、execveとかすれば

Results for execve
nix::unistd::execve
Replace the current process image with a new one (see …
libc::execve
nix::unistd::fexecve
Replace the current process image with a new one (see …
nix::unistd::execveat
Execute program relative to a directory file descriptor …
libc::fexecve
libc::SYS_execve
libc::SYS_execveat
nix::unistd::execv
Replace the current process image with a new one (see …
nix::unistd::execvp
Replace the current process image with a new one and …
nix::unistd::execvpe
Replace the current process image with a new one and …
libc::execv
libc::execle
libc::execvp
libc::execvpe
libc::execl
libc::execlp

こんな具合に出て来る。ググル様も真っ青になる、検索機能が、target/docの中に鎮座してるのね。ただのクリック猿で十分ですよ。

vendor

外部のやつを、プロジェクトdirに鎮座させるサービスコマンド。

[sakae@fb /tmp/shell-sample-code/step1]$ cargo vendor
   Vendoring autocfg v1.0.1 (/home/sakae/.cargo/registry/src/github.com-1285ae84e5963aae/autocfg-1.0.1) to vendor/autocfg
    :
   Vendoring nix v0.23.1 (/home/sakae/.cargo/registry/src/github.com-1285ae84e5963aae/nix-0.23.1) to vendor/nix
To use vendored sources, add this to your .cargo/config.toml for this project:

[source.crates-io]
replace-with = "vendored-sources"

[source.vendored-sources]
directory = "vendor"

こんな具合になる。

[sakae@fb /tmp/shell-sample-code/step1]$ ls
Cargo.lock      Cargo.toml      src/            target/         vendor/
[sakae@fb /tmp/shell-sample-code/step1]$ ls vendor/
autocfg/        cc/             libc/           nix/
bitflags/       cfg-if/         memoffset/

CUIな人は、これでソースの閲覧に集中出来るな。

root of rust

本家本元のソース源、 source だ。けれども、

sakae@deb:~/src$ git clone https://github.com/rust-lang/rust.git
Cloning into 'rust'...
remote: Enumerating objects: 1626274, done.
remote: Total 1626274 (delta 0), reused 0 (delta 0), pack-reused 1626274
Receiving objects: 100% (1626274/1626274), 721.81 MiB | 39.00 KiB/s, done.
Resolving deltas: 100% (1300052/1300052), done.
Updating files: 100% (31730/31730), done.

回線が非常に細いのでcloneするのに5時間はかかった。ふぅーー。


This year's Index

Home