0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

?演算子は開発者の倫理を問う

Posted at

"簡略化された構文ほど、設計者の姿勢が露呈する。"

Rustにおける ? 演算子は、一見すると些細な構文糖衣だ。
Result<T, E> を扱う関数の中で、match を書く代わりに ? を使えば、コードがすっきりする。

let content = std::fs::read_to_string("config.toml")?;

しかしこの ? こそ、Rustという言語の思想が凝縮された構文であり、
同時に開発者に対して「本当にこの関数はエラーを“上に伝える”べきなのか?」という問いを突きつける。

この章では、? 演算子に込められた設計の責任と構文的誠実さを深掘りする。


? の本質は“委譲”である

? を使うとはどういうことか?
それは、「ここで発生したエラーを、自分では処理せず、そのまま呼び出し元へ返す」という意味だ。

fn load_config() -> Result<String, std::io::Error> {
    let content = std::fs::read_to_string("config.toml")?;
    Ok(content)
}

このコードは、**明示的に「この関数は、読み取り失敗を自分では処理しない」**ことを表明している。

つまり ? は構文的な簡略化であると同時に、責任の転送を行う記号である。


「責任を返す」ことの設計的意味

関数から Result<T, E> を返すということは、呼び出し側にエラー処理の責務を課すということだ。
そして ? は、それを構文的に簡素に記述できるが、その設計上の意味は重い。

使うということは:

  • 呼び出し側に設計の判断を委ねる
  • この関数の目的は「処理を継続すること」ではなく「責任を引き継ぐこと」である

つまり、? を使うということは、自分の関数が「判断しない関数である」と明言することに他ならない


? は便利な構文ではなく、“設計の姿勢”を明文化する手段

たとえば ? を多用して構文をスリム化したコードは、たしかに読みやすい。

fn process(path: &str) -> Result<String, std::io::Error> {
    let content = std::fs::read_to_string(path)?;
    let parsed = parse_config(&content)?;
    Ok(parsed)
}

だがこのコードが本当に設計として正しいかは、別問題だ。

  • parse_config のエラーはユーザーに返すべきか?
  • read_to_string の失敗を握りつぶさず、上層へ伝えるのは意図的か?

この問いを無視して ? を並べると、コードは「見やすいが無責任な設計」になる。
構文の簡略化は、設計の透明化とイコールではない。


?が使える場所:構文の安全圏と設計の意識

? が使えるのは、戻り値が Result<T, E> または Option<T> の関数内に限られる。
これは Rust の型システムによる構文レベルでの設計制御である。

fn try_parse(path: &str) -> Option<Config> {
    let content = std::fs::read_to_string(path).ok()?;
    Some(parse(&content)?)
}

ここでの ?None の連鎖を発生させる。
「Noneが出たら終了する」という短絡的制御を、構文で表現している


エラーを「処理する」のか「流す」のか、その判断が設計を分ける

設計者は、あるエラーに対して2つの判断が必要となる:

  1. この層で握りつぶすべきか(match等で明示的に処理)
  2. この層で処理せず、上位へ伝えるべきか(?で委譲)

これらをすべて ? にするということは、「判断をすべて他者に委ねる」ことであり、
場合によっては設計上の怠惰と捉えられかねない。

? を使うということは、“委譲していい”という設計的正当性があることが前提なのである。


expect vs ?:設計のポリシーの差異

let config = std::fs::read_to_string("config.toml").expect("必須ファイル");

この場合は、「エラーを許さない」という強い意思表明。
対して ? は、「失敗するかもしれないが、その責任は自分では持たない」という柔らかい委譲。

この2つの違いは、構文上は小さくても、設計思想としてはまったく異なる


「失敗しても止まらない設計」を実現するか、「失敗したら止める設計」を選ぶか

? を繋げて書くことは、データフローを連鎖させる表現である。
一つでも失敗すれば処理は止まる。これは “失敗は止める”という思想に基づいた構文である。

let config = read_file(path)?
    .lines()
    .map(parse_line)
    .collect::<Result<Vec<_>, _>>()?;

このような連鎖は、「失敗したら戻す」という設計方針があるからこそ意味を持つ。


結語:?は簡易な記号であり、重たい設計判断の表出である

Rustの ? 演算子は、単なる糖衣ではない。
それは「この関数は判断しない」「責任は上に渡す」という設計ポリシーを表明する構文である。

よって、? の使用には常に問いが付きまとう。

  • これは委譲すべき責任か?
  • 上位で処理されることは保証されているか?
  • 設計上、処理の継続か停止か、どちらが正当か?

この問いに誠実に答えたときだけ? は設計の美を宿す。

"構文が簡素であればあるほど、設計者の倫理が問われる。?とはその象徴である。"

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?