Go HTTP 핸들러에서 ResponseWriter는 값이지만 Request는 포인터 인 이유는 무엇입니까?


82

GAE 용 앱을 작성하여 Go를 배우고 있는데 이것은 핸들러 함수의 시그니처입니다.

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

나는 여기 포인터 초보자인데 왜 Request객체가 포인터가 ResponseWriter아닌가? 이런 식으로 할 필요가 있습니까? 아니면 일종의 고급 포인터 기반 코드를 가능하게 만드는 것입니까?

답변:


64

당신이 얻는 w것은 내 보내지 않은 유형에 대한 포인터 http.response이지만 ResponseWriter인터페이스와 마찬가지로 보이지 않습니다.

에서 server.go :

type ResponseWriter interface {
    ...
}

반면에는 r구체적인 구조체에 대한 포인터이므로 참조를 명시 적으로 전달해야합니다.

에서 request.go :

type Request struct {
    ...
}

28

http.ResponseWriter인터페이스이며,이 인터페이스를 구현하는 기존의 유형에 대한 포인터입니다. 즉, 이미 포인터에 의해 "백업"되어 있으므로이 인터페이스에 대한 포인터를 사용할 필요가 없습니다. 이 개념은 여기 에서 go 개발자 중 한 명이 설명합니다. http.ResponseWriter를 구현하는 유형이 포인터가 될 필요는 없지만 적어도 go http 서버 내에서는 실용적이지 않습니다.

http.Request은 인터페이스가 아니라 구조체 일뿐입니다.이 구조체를 변경하고 웹 서버가 이러한 변경 사항을 확인하도록하려면 포인터가되어야합니다. 구조체 값일 경우 코드를 호출하는 웹 서버가 볼 수없는 복사본을 수정합니다.


1
나는 이것이 옳다고 생각하지 않는다. 포인터 뒤에있는 값은 값과 동일한 인터페이스를 구현할 수 있으므로 여기서 구분할 필요가 없습니다. 기본 유형이 int 인 유형은 포인터가 아니거나 하나에 의해 뒷받침되지 않고 인터페이스를 만족시킬 수 있습니다.
nemo

2
글쎄요, 인터페이스를 구현하는 모든 것이 포인터가되어야 한다는 의미는 아닙니다 . 유용한 작업을 수행하기 위해 인터페이스 뒤에있는 값의 상태를 수정해야하는 ResponseWriter와 같은 것입니다.이 경우 해당 값은 포인터 유형이어야합니다 (링크 된 블로그 게시물에서도 언급했듯이). 그러나 예, http.ResponseWriter는 이론적으로 int에 의해 구현 될 수 있습니다 (그 메서드는 해당 int 값을 변경할 수 없음).
nos

이 답변은 매우 유용했습니다. 이 링크는 포인터를 처음 접하는 사람들에게 명확한 설명과 함께 값을 매길 수 없습니다. "즉, 명시적인 마커는 없지만 인터페이스 객체는 종종 포인터처럼 작동합니다. 실제로 무슨 일이 일어나고 있는지 이해하기 전까지는 혼란 스러울 수 있습니다."
Cody Django

1
Request에 대한 부분이 실제로 잘못되었습니다. 내 답변을 참조하십시오. stackoverflow.com/a/56875204/989991
joakim 19

6

여기와 다른 곳에서 다른 많은 답변에서 올바르게 언급했듯이 ResponseWriter인터페이스이며 이에 대한 의미는 SO 답변 및 블로그 에 자세히 설명되어 있습니다.

(그런 일이 실제로 존재하지 않지만 내가 주소로하고자하는 이유 요청 "참조"에 의해 전달하는 큰 위험 - 오해, 여기에 내가 느낌이다 이동 ) 우리가 변경하려면 "이다 서버에 표시됩니다. "

몇 가지 답변을 인용합니다.

[..] 그것은 단지 구조체이고, 우리가이 구조체를 변경하고 웹 서버가 이러한 변경 사항을 보게하기를 원하기 때문에 그것은 포인터 [..] SO

우리는 단지 값 대신에 참조로 전달되도록하는 [...] 서버에 표시되도록 핸들러 필요에 의해 변경 요구에 [...] SO

이것은 잘못된 것입니다 . 실제로 문서는 요청을 변조 / 변이하는 것에 대해 명시 적으로 경고합니다 .

본문을 읽는 경우를 제외하고 핸들러는 제공된 요청을 수정해서는 안됩니다.

그 반대 죠? :-)

예를 들어 미들웨어 체인의 다음 핸들러로 전달하기 전에 추적 헤더를 추가하는 것과 같이 요청을 변경하려면 요청 을 복사 하고 복사 된 버전을 체인 아래로 전달해야합니다.

수신 요청을 수정할 수 있도록 동작을 변경하라는 요청 Go 팀과 함께 제기 되었지만 이와 같이 변경하면 적어도 일부 기존 코드가 예기치 않게 중단 될 수 있습니다.

사람들에게 요청을 변경하지 말라고 명시 적으로 지시하는 경우 왜 포인터를 사용합니까? 성능 , Request큰 구조체이며 특히 마음에 긴 미들웨어 체인, 성능이 가져올 수있는 복사. 팀은 균형을 유지해야했지만 이상적인 솔루션은 아니지만 여기서는 성능 측면에서 절충안이 분명합니다 (API 안전성 대신).


이것이 마침내 나에게 이해가 된 대답입니다.
Jimi

1

Request에 대한 포인터 인 이유는 간단합니다. 핸들러에 의한 Request 변경은 서버에 표시되어야하므로 값 대신 참조로만 전달합니다.

net / http 라이브러리 코드를 자세히 살펴보면 ResponseWriter가 내 보내지 않은 구조체 응답에 대한 인터페이스이고 값이 아닌 참조로 구조체를 전달한다는 것을 알 수 있습니다 (응답에 대한 포인터를 전달 함). . ResponseWriter는 핸들러가 HTTP 응답을 생성하는 데 사용하는 인터페이스입니다. ResponseWriter를 백업하는 실제 구조체는 내 보내지 않은 http.response 구조체입니다. 수출되지 않기 때문에 직접 사용할 수 없습니다. ResponseWriter 인터페이스를 통해서만 사용할 수 있습니다.

즉, 두 매개 변수가 모두 참조로 전달됩니다. 메서드 시그니처는 구조체에 대한 포인터에 대한 인터페이스 인 ResponseWriter를 사용하므로 마치 값으로 전달 된 것처럼 보입니다.


나에게 원래의 대답보다 더 많은 의미가 있습니다
벤 카타 SSKM Chaitanya

Request에 대한 부분이 실제로 잘못되었습니다. 내 답변을 참조하십시오. stackoverflow.com/a/56875204/989991
joakim 19

0

Request객체가 포인터로 전달되는 주된 이유 는 Body필드 라고 생각 합니다. 주어진 HTTP 요청에 대해 본문은 한 번만 읽을 수 있습니다. 경우 Request개체가 복제 된,이 포인터로 전달되지 않은 경우가있을 것입니다, 우리는 몸에서 읽은 양에 대한 다양한 정보를 두 개체있을 것입니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.