引数の位置での存在型は、 Interface や基底クラスと関数テーブルのようなものとかなり近く見えますね。
対して戻り値の位置では、 interface そのものを返すことはできない(と思う)し、基底クラスのポインタを返すにせよ「基底クラスのポインタである」という具体的な内部実装は漏れるわけです。
加えて、こういった実行時の多相だとオーバーヘッドが出てしまうけど、存在型ならユーザが名前を示せないだけでコンパイラは具体的な型を知っているので、実行時オーバーヘッドがない
たとえば C++ のクロージャは型名を指定できないので、 auto で雑に受けるか std::function で受けるわけですが、前者では関数であると縛りをつけられないし、後者だと実行時オーバーヘッドがあります。
Rust で同じことをすると let による型指定なしの束縛と Box<dyn Fn()> などが相当するわけですが、これ以外にも impl Fn() という指定もできます。
たとえばクロージャを返す
fn f() -> impl Fn() {
|| println!("hello")
}
は、「何度でも呼べる関数として振る舞う何かを返す」ということは述べるけど、 std::function のようなオーバーヘッドはない (わざと付けることもできるけど)
めっっっっちゃ雑に言うなら、存在型は「引数の型としては型パラメータとして振る舞い、戻り値の型としては『コンパイラにしか指定できない内部的な名前』として振る舞う」みたいなイメッジでとりあえず困らないかも
https://mastodon.cardina1.red/@lo48576/101920508524525423
この説明はちょっと違ったかも、存在型はたとえば「ユーザが指定した Interface を実装する何らかの型」とかであって、ポインタ+仮想関数テーブルとはまた別の意味になっているので