Go HTTPハンドラーで、ResponseWriterが値であるのに、Requestがポインターであるのはなぜですか?


84

私はGAE用のアプリを書くことでGoを学んでいます。これは、ハンドラー関数のシグネチャです。

func handle(w http.ResponseWriter, r *http.Request) {}

私はここではポインタ初心者ですが、なぜRequestオブジェクトはポインタなのResponseWriterですか?このようにする必要はありますか、それともある種の高度なポインタベースのコードを可能にするためだけですか?

回答:


66

何を取得することはw非エクスポート型へのポインタでhttp.responseはなくとしてResponseWriter表示されていないインタフェースが、あります。

server.goから:

type ResponseWriter interface {
    ...
}

一方、rは具象構造体へのポインタであるため、参照を明示的に渡す必要があります。

request.goから:

type Request struct {
    ...
}

28

http.ResponseWriterインタフェースであり、このインタフェースを実装し、既存のタイプがポインタです。つまり、このインターフェイスはすでにポインタによって「バックアップ」されているため、このインターフェイスへのポインタを使用する必要はありません。この概念は、ここでgo開発者の1人によって少し説明されています。http.ResponseWriterを実装する型はポインターである必要はありませんが、少なくともgohttpサーバー内では実用的ではありません。

http.Requestはインターフェースではなく、単なる構造体です。この構造体を変更してWebサーバーにそれらの変更を認識させたいので、ポインターである必要があります。構造体の値だけの場合は、コードを呼び出すWebサーバーが認識できないコピーを変更するだけです。


1
私はこれが正しくないと思います。ポインタの背後にある値は、値と同じようにインターフェイスを実装できるため、ここで区別する必要はありません。基本型がintの型は、ポインターになったり、ポインターに支えられたりすることなく、インターフェースを満たすことができます。
nemo 2012年

2
まあ、私はインターフェースを実装するすべてのものがポインターでなければならないことを意味するつもりはありませんでした。何か便利なことをするためにインターフェースの背後にある値の状態を変更する必要があり、その値がポインター型である必要があるResponseWriterのようなもの(私がリンクしたブログ投稿も言っているように)。しかし、はい、http.ResponseWriterは理論的にはintによって実装できます(そのメソッドはそのint値を変更できません)。
nos 2012年

この答えはとても役に立ちました。そのリンクは貴重であり、ポインタを初めて使用する私たちにとっては明確な説明があります。「つまり、明示的なマーカーはありませんが、インターフェイスオブジェクトはポインタのように機能することがよくあります。これは、実際に何が起こっているのかを理解するまで混乱する可能性があります。」
コーディジャンゴ

1
要求に関する部分は、実際に間違っている、私の答えを参照 stackoverflow.com/a/56875204/989991
ヨアキム

7

ここや他の場所で他の多くの回答で正しく言及されているようにResponseWriter、インターフェースであり、これの意味はSOの回答とブログで詳細に説明されています。

私が対処したいのは、ここでの大きな、そして危険な誤解であると感じていることです。リクエストが「参照」によって渡される理由は(Goには実際には存在しませんが)、「変更を加えたい」ということです。サーバーに表示されます」。

いくつかの答えを引用します:

[..]これは単なる構造体であり、この構造体を変更してWebサーバーにそれらの変更を認識させたいので、ポインターである必要があります[..] SO

[..]ハンドラーによるRequestへの変更はサーバーに表示される必要があるため、値ではなく参照によってのみ渡します[..] SO

これは間違っています; 実際、ドキュメントはリクエストの改ざん/変更に対して明示的に警告しています:

本文を読み取る場合を除いて、ハンドラーは提供されたリクエストを変更しないでください。

まったく逆ですよね?:-)

ミドルウェアチェーン内の次のハンドラーに渡す前にトレースヘッダーを追加するなど、リクエストを変更する場合は、リクエストをコピーして、コピーしたバージョンをチェーンに渡す必要があります

着信リクエストの変更を許可するように動作を変更するリクエストGoチームで提起されましたが、このような変更を行うと、少なくとも一部の既存のコードが予期せず破損する可能性があります。

リクエストを変更しないように明示的に指示しているのに、なぜポインタを使用するのですか?パフォーマンスはRequest大規模な構造体であり、それは特に念頭に置いて、長いミドルウェア鎖で、パフォーマンスをダウンさせることができますコピーします。チームはバランスをとる必要がありましたが、これは間違いなく理想的なソリューションではありませんが、トレードオフは明らかにここでのパフォーマンスの側面にあります(APIの安全性ではありません)。


1
これが私にとって最終的に理にかなっている答えです。
ジミ

2

それがRequestへのポインターである理由は単純です。ハンドラーによるRequestへの変更はサーバーに表示される必要があるため、値ではなく参照によってのみ渡します。

net / httpライブラリコードを掘り下げると、ResponseWriterはエクスポートされていない構造体応答へのインターフェイスであり、値ではなく参照によって構造体を渡していることがわかります(応答へのポインタを渡します)。 。ResponseWriterは、ハンドラーがHTTP応答を作成するために使用するインターフェースです。ResponseWriterをバックアップする実際の構造体は、エクスポートされていない構造体http.responseです。エクスポートされていないため、直接使用することはできません。ResponseWriterインターフェースを介してのみ使用できます。

つまり、両方のパラメーターが参照によって渡されます。メソッドシグネチャが構造体へのポインタへのインターフェイスであるResponseWriterを受け取るだけなので、値によって渡されたかのように見えます。


私への最初の答えよりも理にかなっています
Venkata SSKMChaitanya19年

要求に関する部分は、実際に間違っている、私の答えのstackoverflow.com/a/56875204/989991見る
ヨアキム

0

Requestオブジェクトがポインタとして渡される主な理由はBodyフィールドだと思います。特定のHTTPリクエストに対して、本文は1回しか読み取ることができません。場合はRequest、オブジェクトがクローン化された、それはポインタとして渡されていなかった場合等において考えられるように、私たちは、身体から読み出されたどのくらいに関するさまざまな情報を持つ2つのオブジェクトを持っているでしょう。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.