문학동네

284쪽

20.11.21 ~ 12.27(37일)

 

 

"롤리타"와 "창백한 불꽃"에 이어 세번째로 읽은 나보코프의 장편 소설이다. 앞선 두 작품보다는 조금 더 짧고 가볍게 읽을 수 있다. 그러나 이 작품도 마찬가지로 신뢰할 수 없는 화자가 1인칭으로 진행하는 소설이며, 초반부에는 도대체 무슨 내용으로 전개되는건지 가늠하기 어려운 특징이 있다. 그래서 나보코프 작품치고는 쉽지만, 일반적인 소설 기준으로는 읽기 쉬운편은 아니라고 할 수 있다. 

 

가장 큰 특징으로, 화자가 자신을 뛰어난 작가라고 자평하면서, 소설 작법에대해 이리저리 떠드는 장면들이 있다. 이번 장은 어떻게 시작하는게 좋을지 이런저런 안을 내세우면서 각 안들에 대해 평하기도 하는데, 상당히 재미있다. 또한 유독 도스토예프스키와 푸쉬킨에 대한 패러디가 많은데, 아무래도 이 점은 작가의 성향이 묻어나는 것 같다. 특히나 도스토예프스키는 너무하다 싶을정도로 까내린다.

 

전반적으로 나보코프의 천재성이 잘 들어나는 개성있고 뛰어난 작품.

'' 카테고리의 다른 글

감염 도시 - 스티븐 존슨  (0) 2021.01.25
스토너 - 존 윌리엄스  (0) 2021.01.07
엠마 - 제인 오스틴  (0) 2020.11.22
여학생 - 다자이 오사무  (0) 2020.09.27
창백한 불꽃 - 블라디미르 나보코프  (0) 2020.08.31

0. 들어가기 앞서

이 글은 비전문가가 쓴 글이다. 앞으로 나는 전문가인 것처럼 나의 주장을 펼칠 테지만, 기본적으로 비전문가의 글임을 잊지 않았으면 좋겠다. 또한 이글에서 나는 많은 데이터와 주장들을 인용할 것인데, 전문적인 인용 방식을 무시하고, 보기 편한 기사들을 링크 방식으로 인용할 것이다. 심지어 나무 위키를 인용하는 경우도 있을 수 있다. 물론 내가 인용하는 링크들에는 원문이나 데이터 출처가 있으므로, 원한다면 그것들을 참고해도 좋다.

 

한 가지 더, 이 글의 주장은 정치 성향과 무관하다. 그럼에도 불구하고 딱 한 가지 선을 미리 그어두고 싶은 주장이 있는데, 바로 "국가가 코로나에 대한 공포심을 자극시켜 이용해 먹는다"라는 식의 음모론이다. 이러한 종류의 멍청한 주장에 굳이 반박이 필요할 것 같진 않다.

 

개요

이 글을 쓰는 시점인 2020년 12월 17일 현재, 한국에서는 확진자 1000명대를 며칠 전에 돌파하고, 이어지고 있는 상황이며, 방역 정책도 2.5단계를 넘어, 3단계 격상을 고심 중이다. 하지만 나는 현재의 방역 수준이 지나치게 과잉 대응이며, 따라서 방역 수준을 낮추고 방역의 패러다임을 전환해야 한다고 주장한다. 여기에 대해 나의 핵심 논리를 요약하면 다음과 같다.

 

1) 방역 정책의 목적은 (장기적으로) 확진자 수를 줄이는 것이 아니며, 애당초 이는 불가능한 일이다. 주객전도 하지 말고 중증 환자 중심으로 사망자를 줄이는데 목적을 둬야 한다. 이를 통해 의료 자원의 과부하도 막을 수 있다.

2) 현재 방역 정책은 전염병의 심각성에 비해 지나치게 과잉 대응이다. 방역 정책으로 인한 경제적 피해가 전염병 자체의 피해보다 훨씬 더 크다.

3) 국내의 사회적 거리두기는 고통 분담의 측면에서 정의롭지 않다.

 

가장 강력한 논리는 2)이지만, 우선 이 글에선 1)만을 다룬다. 

 

방역 정책과 누적 확진자의 상관 관계

본격적으로 들어가기 전에, 우선 유럽의 프랑스, 독일, 체코의 그래프를 보자. 그래프를 제공하는 사이트는 여러 곳이지만, 가장 간편한 구글의 것을 참조했다.

프랑스

 

독일
체코

그래프와 더불어, 각 국에서 1차 유행이 일단락된 6월 1일 기준의 확진자 수는 다음과 같다.

 

  6/1 기준 누적 확진자 6/1 기준 누적 확진자(100만명 당)
프랑스 151,315 2,258
독일 183,564 2,211
체코 9,273 867
누적 확진자 출처 : 코로나보드 / 100만 명당은 직접 계산함

 

먼저 체코, 폴란드, 헝가리 등의 동구권은 1차 확산 당시 서유럽에 비해 훨씬 성공적인 방역을 했다고 평가받았고, 실제로 확진자와 사망자가 모두 적었다. 그러나 이번 2차 확산에선 프랑스조차 뛰어넘는 확산세를 보인다. 가장 심한 경우인 체코는 인구 대비 누적 확진자 수에서조차 프랑스를 한참 넘어섰다. 

 

반면 독일은 1,2차 유행 당시 모두 유럽 주요국들(영국, 프랑스, 이탈리아, 스페인 등)중 가장 선제적이고 성공적인 방역을 했다고 평가받았다. 실제로 현시점에선 유럽 주요국보다 누적 확진자수가 적다. 하지만 다른 유럽 국가들이 2차 유행이 정점을 지나 소강상태로 점점 향하는 방면에, 독일은 1달 넘게 정점에 머무르며 방역에 대한 평가가 반전되고 있다.

 

가장 느슨하고, 엉망인 방역을 한 것으로 평가받은 프랑스는, 실제로 2차 유행 당시 주요 국중 가장 높은 확산세를 보였다. 하지만 그만큼 빠르게 2차 유행이 종료되었다.

 

방역 수준만 보면 서유럽 주요국 < 독일 <= 동구권 정도로 평가할 수 있을 것이다. 그런데 왜 동구권 국가들이 서유럽보다 2차 확산에 피해가 클까? 왜 독일은 2차 유행이 끝이 나지 않고 오래 지속되고 있을까? 답은 아주 간단하다. 장기적인 관점에서 누적 확진자는 방역 수준과 상관이 없기 때문이다. 누적 확진자 수는, 각국의 기후조건, 인종 구성, 인구 밀도 등에 의해 결정되지, 방역 정책에 의해 바뀌지 않는다. 즉, 현재까지의 누적 확진자 수가 적다면, 앞으로는 더 많이 발생하게 된다. 이 간단한 주장에 따르면 각 나라들의 확진자와 유행 수준에 대해 설명이 충분히 가능하다. 몇 가지 예시를 보자.

 

1) 방역 정책이 훨씬 더 강력한 유럽/미국은 왜 한국/일본보다 확진자 수가 훨씬 많은가? 

=> 유전적 차이. 실제로 현지 한인들의 피해는 극히 미미한 것으로 알려져 있다.

 

2) 왜 방역 시스템이 제대로 갖춰지지 않은 대다수의 동남아시아 국가들은 확진자 수가 적은가?

=> 기후 차이. 바이러스는 기본적으로 열에 굉장히 약하다. 겨울이 유행기인 것과 같은 이유.

 

3) 왜 방역이 강력했던 국가들의 유행이 주변국보다 더 오래가는가?

=> 누적 확진자 수가 충분할 때까지 유행이 종료되지 않는다. 

 

4) 왜 한국은 이전보다 강한 방역 정책을 시행 중인데 확진자는 점점 늘어나는가?

=> 누적 확진자 수가 너무 적다. 

 

 

전염병의 특성을 통해서도 설명이 가능하다. 백신이 없는 경우, 전파력이 충분히 강한 병원체는 충분히 긴 시간이 있으면 모든 인구에 접촉이 가능하다. 병원체에 어떤 사람이 접촉하게 될 경우, 감염 여부는 방역 정책이랑 무관하고 그 사람의 면역력에 달려있다. 여기서 전파력이 충분히 강하다 = 인간의 노력으로 완전 박멸이 불가능하다고 봐도 무방하다. 잘 생각해보면 자명한 일인데, 완전 종식(종 간격을 넘는 것도 포함)이 불가능하다면, 결국 계속 전파되어 충분히 많은 인구가 항체를 지닌 시점까지 전파되게 되기 때문이다. 

 

한국의 사회적 거리두기

google social distancing mobility report / 차트는 엑셀로 직접 만듦

 

구글에서는 사회적 거리두기에 따른 이동량 감소 데이터를 국가마다 제공한다. 구글에서는 이 데이터를 전문적인 용도로 인용하는 것을 자제하라고 권고하였지만, 서두에서 밝혔다시피 이 글은 전문적인 글이 아니다. 그리고 국가 간 사회적 거리두기 수행도를 살펴보는데 이보다 더 좋은 데이터가 없기도 하다. 구글 데이터는 몇 가지 항목을 더 제공하는데, 가장 중요한 두 가지 항목만 추렸다. 예컨대 추가로 공원의 이동량 데이터를 제공한다.(참고로 공원은 풍선효과로 이동량이 대체로 증가한 경우가 많다.)

 

거리두기 수행도에 따른 시뮬레이션을 분석한 글도 참고하면 좋다. 직관적인 시뮬레이터를 제공하는 사이트도 있다. 

 

위 시뮬레이터들을 참고하면, 아무리 적어도 이동량 50% 감소부터 거리두기가 의미 있음을 알 수 있다.(첫 링크 글은 아예 75% 감소 이하는 취급하지도 않는다.) 이 점을 고려하면 국내에서 (자발적인) 사회적 거리두기가 의미 있는 수준으로 수행된 적은 단 한순간도 없었음을 알 수 있다. 위 데이터들을 보면 30% 이상 이동량이 감소한 적은 없다고 볼 수 있으니 말이다. 사실 이는 제대로 된 민주주의 국가에서는 당연한 일이라고 할 수 있겠다. 

 

반면 프랑스는 어느 정도 의미 있는 수준의 감소가 있었다. 주변 유럽 국가들도 비슷한 수준의 이동량 감소를 보인다. 이는 이 국가들이 우리의 3단계 이상, 계엄령에 준하는 수준의 강제 정책을 실시했기 때문이다. 그럼에도 불구하고 이 국가들조차 확산세를 막을 수는 없었다.

 

방역 정책의 의미

그렇다면 방역 정책은 의미가 없단 뜻인가? 그렇지 않다. 방역 정책은 유행 속도를 낮춤으로서, 1) 대응 체계를 갖출 시간을 벌고, 2) 의료 자원의 과부하를 막는다. 영미권에서는 flattening the curve라고 표현하는데, 말 그대로 그래프의 기울기를 낮추는 것이 목적이란 뜻이다. 결코 면적(=누적 확진자)을 줄이는 것이 목적이 아니다! 좀 더 직관적으로 표현하자면, 방역 정책은 확진자를 줄이는 것이 아니라, 사망자를 줄이는데 중점을 두고 있다고 할 수 있다. 

 

flattening the curve라는 표현에서 한 가지 더 얻을 수 있는 인사이트가 있다. 바로 확진자수가 극단적으로 낮은 것이 좋은 것만은 아니란 점이다. 위의 체코의 사례가 이 경우라고 할 수 있다. 체코는 1차 유행 당시 확진자수가 너무 낮아서, 즉 flattening이 과도해서, 2차 유행에서 확진자가 지나치게 급증하였다. 체코가 1차 유행 당시 조금 더 느슨한 방역을 하였다면, 확진자 수가 조금 더 나왔을 것이고, 2차 유행의 폭증은 어느 정도 완화되었을 것이다. 

 

국내 방역 정책의 문제점

이제 국내의 상황을 보자. 우선 1) 대응 체계를 갖출 시간을 번다는 점은 1차 유행 당시 큰 사망자를 내지 않고 충분히 잘 이루어졌다. 문제는 2) 의료 자원의 과부하를 막는다라는 부분이다. 의료 자원의 과부하를 막는 것은, 사망자 수를 줄이는 데 가장 중요하다. 

 

현재 일일 확진자는 1000명대를 막 돌파한 상황임에도, 벌써부터 의료진과 병상 자원의 심각한 과부하가 걸려있다. 1000명은 한국처럼 의료 시스템 최강국이 과부하 걸릴만한 숫자가 결코 아니다. 독감을 예시로 들면, 1달 동안 확진자 125만 명, 사망자 대략 1000여 명이 발생한 사례가 이미 있다. 이는 결코 특별한 사례가 아니고, 별다른 뉴스도 되지 못하고 지나갔으며, 당연히 의료 자원에 심각한 과부하가 걸리지도 않았다. 독감은 (불완전하지만) 백신과 치료제가 있는 상황임을 감안해도, 현재 코로나 19 대응 체계가 얼마나 심각한 과잉 대응인지 짐작할 수 있다. 

 

이러한 과부하의 원인은 현재 한국의(그리고 대부분 다른 국가들도 마찬가지로) 코로나 대응 체계가 주객전도되어 확진자 수를 줄이는데 모든 초점이 맞춰져 있기 때문이다. 이러한 방향은 사회적 비용이 너무나도 많이 발생하는 것이 가장 큰 문제지만, 심지어 방역 측면에서도 바람직하지 않다. 현재와 같이 과부하된 상황이 이어지면, 겨우 2~3000여 명대에서 의료 체계가 붕괴되는 심각한 상황이 발생할 수 있다. 현재 의료 자원에 과부하가 걸린 이유는 확진자가 많아서가 아니다. 대응 수준이 지나치게 높기 때문이다. 

 

대안

확진자를 줄이려는 노력을 줄이고, 중증환자 위주로 자원을 투자해야 한다. "선제적으로" 감염 고리를 막으려고 과도한 노력을 해서는 안된다. 어느 정도 전파가 발생하는 것은 필연적이란 점을 받아들여야 한다.

 

여기서 내가 주장하는 것은, 방역을 포기하라거나 역학조사를 중지하라는 뜻이 아니다. 확산 저지에 투자하는 자원을 훨씬 더 줄이고, 경제와 중환자 관리에 투자하라는 것이다. 그리고 조금 더 장기적으로 바라보며 의료 자원을 아끼라는 말이다. 예컨대 다음과 같은 조치들이 있을 수 있겠다.

 

1) 자가 격리 기간 완화 / 자가 격리 대상 기준 완화

2) 중증 환자를 제외하고, 통원 치료

3) 경제 활성화 조치 => 이 글의 주제에선 벗어나니 상세히 적지는 않는다.

 

물론 지금은 많이 늦긴 했다. 조금 여유가 있을 때(9~11월에!) 미리 체계를 정비하고, 방역 수준을 낮춰서 체력과 자원을 확보했으면 훨씬 좋았을 것이다. 지금이라도 방역의 패러다임을 전환하여 의료 자원의 과부하를 막을 필요가 있다. 그렇지 않으면 앞으로의 유행이 심화함에 따라 자원과 체력 고갈로 대응 체계 자체가 붕괴해버릴 위험이 있다.

 

예측

어떠한 주장은 검증 가능해야만 과학적으로 의미가 있다. 이 글의 주장에서도 몇 가지 검증 가능한 예측이 발생할 수 있는데, 가장 직관적인 국내의 사례로 한 가지 예측을 해보자. 바로 한국과 일본의 코로나 진행 상황에 대한 예측이다.

 

이 글을 작성 중인 2020년 12월 17일 시점에서, 한국은 누적 확진자 45,553 / 100만 명당 확진자 910명이다. 일본은 누적 확진자 188,000명 / 100만 명당 확진자 1,481명이다. 현시점에서 한국과 일본의 인구당 누적 확진자수를 비교하면 일본이 한국보다 대략 63% 정도 많다. 현재 일본은 고투 트래블을 며칠 전에 일시 중단하긴 했으나, 명백히 한국보다 선제적이지도 않고, 방역 대응 수준도 낮다. 아직까지는 일본의 확산세가 더 강하기도 하다. 따라서 방역 정책으로 누적 확진자 수를 줄일 수 있다면, 한국과 일본의 차이는 점점 벌어지게 될 것이라고 예측할 수 있다.

 

하지만 이 글의 주장에 따르면, 방역 정책으로는 누적 확진자 수를 줄일 수 없다. 일본과 한국은 기후환경이나 유전적 조건이 비교적 가까운 편이므로, 유행이 끝나는 시점에서 누적 확진자도 비슷해야 한다. 물론 조건이 완전히 같지는 않고, 어느 정도 오차는 있을 수 있으니, 이를 감안하면 대략 +-25% 안으로 들어와야 의미가 있을 수 있지 않을까 싶다. 대략 이번 절기의 유행이 완전히 끝나는 시점을 4월 말로 잡자. 이 글의 주장이 맞다면, 4월 말경에 일본의 인구 대비 누적 확진자수가 한국과 비교했을 때 +-25% 안으로 들어올 것이다. 즉, 일본 / 한국 <= 25% or 한국 / 일본 <= 25%이 될 것이라고 예측한다. 

 

수정) 4월 말은 지나치게 안일하게 설정하였다. 가장 큰 원인으로는 변이를 전혀 고려못했고, 지난 겨울의 유행이 한국이나 일본이나 생각보다 크게 약하게 지나갔기 때문이다. 4월 말 -> 현 유행이 어느 정도 잠잠해지는 시점으로 생각을 하면 되겠다.(물론 유행 자체는 매년 발생할 것이다.)

 

특별한 일이 없다면(한국이 확진자 집계를 포기한다던가), 이 주장의 범위에서 벗어날 경우 이 글의 "방역 정책과 누적 확진자의 상관 관계" 주장은 완전히 틀린 것이라고 봐도 무방하다.

 

참고 : 백신

코로나19는 유례없이 빠른 속도로 백신이 개발 완료되었다. 이는 모더나와 화이자의 코로나19 백신이 RNA백신이라는 새로운 방식을 사용했기 때문이다. RNA 백신은 기존의 병원체를 약화시킨 백신보다는 안정성면에서 약간 더 우월하다고 할 수 있다. 그럼에도 불구하고 통상 18개월 이상이 걸리는 임상 3상을 반년만에 끝낸 것은 결코 정상적이지 않다. 현시점에서 보면, 1년 이내의 단기간의 부작용은 충분히 테스트 가능했을 테지만 1년 이상의 시차를 두고 발생하는 백신의 부작용은 테스트하지 못했다고 할 수 있다. 백신은 벌써부터 접종하는 것은 안전성 측면에서 지나치게 시기상조이다. 이는 백신 반대 운동을 펼치는 멍청이들과는 명백히 다른, 충분히 근거 있는 우려이다.

 

게다가 시기상으로도 백신 접종에 의문이 든다. 현재 가장 먼저 백신 접종을 시작한 영국을 보자. 영국은 이미 유행의 정점을 지났다. 코로나 19가 독감과 거의 유사한 유행 시기를 보인다는 점을 감안하면, 이미 영국은 지금부터 접종을 시작해도 이번 절기의 유행에 대처하기는 늦었다. 다음 절기의 유행에 대비해 21년 9~10월에 접종을 시작하는 것이 현재로서는 가장 이상적일 것이다. 사실 이것도 기존의 백신 개발 절차를 생각하면 지나치게 빠른 감이 있긴 하지만...

 

그럼에도 불구하고 백신 접종을 서두르는 것은, 아마 다른 국가와의 경쟁심으로 인한 조바심과 상징적 효과 때문이지 않을까 싶다. 이번 절기의 유행은 백신 접종으로 별다를 바 없겠지만, 백신 접종이 시작되었다는 상징적 효과로 인해 경제 재개화를 앞당기는 효과를 노리고 있지 않을까 싶다. 물론 여론에 못 이기는 면도 있을 거고.

 

참고 : 다른 전염병과의 비교

  한국 연간 사망자 전 세계 연간 사망자 치사율 비고
인플루엔자 29004000~5000 291,000 ~ 646,100 대략 0.1% 독감은 확진자가 너무 많아서, 명확한 통계가 없고 관련 연구들만 존재함.
코로나19 634
(20/12/27 기준)
1,655,497
(20/12/27 기준)
한국 1.36% 
전 세계 2.22%
(20/12/27 기준)
기저 질환을 고려하지 않은 수치
결핵 1800
(2018)
2209
(2015)
1,600,000
(2017)
한국 3~5%
전세계 20%
OECD 평균 1%
(2018)
의료시스템이 부실한 후진국에서 주로 발병/사망하나, 한국은 예외적으로 사망자 수가 많음.
코로나19 데이터 출처 : 코로나보드

 

호흡기 질환인 3가지의 전염병들을 비교해 보았다. 우선 코로나19의 수치에는 주의할 점이 있다. 우선 코로나19는 2020년 1~2월에는 유행이 크게 퍼지지 않았고, 아직 12월도 절반 정도 남았으니 온전한 1년의 수치가 아니다. 한 가지 더, 코로나19의 사망자수는 기저 질환을 고려하지 않은 수치이다. 즉, 사망의 주된 원인이 코로나19가 아님에도, 코로나19에 감염되면 사망자 수로 합산된다. 따라서 독감/결핵과 같은 기준을 적용한다면, 사망자 수치는 절반 이하로 훨씬 더 낮아진다.

 

결핵은 치사율이 꽤 높은 편이라 한국에서는 비교적 명확한 통계가 존재한다. 반면에 독감은 명확한 통계가 존재하지 않는다. 예컨대 독감-> 폐렴으로 진행되는 가장 흔한 케이스의 경우, 일반적으로는 폐렴이 사망원인으로 통계를 작성한다.(폐렴은 국내 사망원인 3위이다.) 하지만 이런 경우 독감의 사망자수가 지나치게 낮게 측정될 수 있는 문제점이 있다. 독감이 주된 원인으로 폐렴으로 진행되어 사망하는 경우가 상당히 많기 때문이다. 그래서 이 점을 보완하여 독감이 주된 원인으로 사망한 수를 조사한 연구들은 대략 한국에서의 사망자를 3000~5000명 정도로 추정한다. 

 

전 세계 기준으로는, 특히나 독감 관련 통계가 부실하다. 291,000 ~ 646,100을 제시한 연구에서도, 33개국 만을 표본으로 조사했을 뿐으로 한계점이 명확함을 밝히고 있다. 정확한 수치는 알기 어렵지만, 적어도 결핵보다는 사망자가 많을 것으로 확신할 수 있다. (결핵 위험국인 한국조차 독감 사망자가 더 많다.)

 

대략 정리하면, 병에 걸린 상태의 위험성(대표적으로 치사율)은 결핵 > 코로나19 > 독감이고,

피해를 끼치는 정도는(대표적으로 사망자 수)은 독감 > 결핵 > 코로나19라고  할 수 있겠다. 

'기타' 카테고리의 다른 글

한성 르누아르 4800H 올데이롱 TFX5470H 후기  (0) 2020.07.08

[0]

Rust의 CellRefCell은 컴파일러를 속여, interior mutability를 제공하는 기능이다. 여기에서는 Rust 코드 내에서 어떻게 이를 제공하는지 알아보자. Rust 버전은 1.44.0이다.

 

[1]

우선 Cell은 src/libcore/cell.rs에 정의되어 있다.

pub struct Cell<T: ?Sized> {
    value: UnsafeCell<T>,
}

...
impl<T> Cell<T> {
...
    pub fn replace(&self, val: T) -> T {
        // SAFETY: This can cause data races if called from a separate thread,
        // but `Cell` is `!Sync` so this won't happen.
        mem::replace(unsafe { &mut *self.value.get() }, val)
    }

    pub fn set(&self, val: T) {
        let old = self.replace(val);
        drop(old);
    }
...
}
    

Cell은 간단하게 UnsafeCell을 wrapping 하고 있다. interior mutablility를 제공하는 set() 메소드가 중요한데(파라미터가 &mut self가 아니라 &self임을 주목하자), 여기서는 replace() 메소드를 호출하고, replace()에서는 UnsafeCell의 get 메소드를 호출한다. 

#[lang = "unsafe_cell"]
#[stable(feature = "rust1", since = "1.0.0")]
#[repr(transparent)]
#[repr(no_niche)] // rust-lang/rust#68303.
pub struct UnsafeCell<T: ?Sized> {
    value: T,
}

impl<T: ?Sized> UnsafeCell<T> {
...
    pub const fn get(&self) -> *mut T {
        // We can just cast the pointer from `UnsafeCell<T>` to `T` because of
        // #[repr(transparent)]. This exploits libstd's special status, there is
        // no guarantee for user code that this will work in future versions of the compiler!
        self as *const UnsafeCell<T> as *const T as *mut T
    }
...
}

Rust의 interior mutability의 가장 핵심적인 struct인 UnsafeCell과 가장 핵심적인 메소드인 get()이다. UnsafeCell 멤버는 그저 value를 wrapping하고 있을 뿐이므로 메소드를 보자.  get()은 파라미터로 imuutable인 &self를 받아서 mutable raw pointer인 *mut T를 리턴한다. 그런데 내부에서는 단순히 캐스팅만을 할 뿐이다. 3단계의 캐스팅을 거쳐, 마지막에는 *const T를 *mut T로 캐스팅하는데, 당연하지만 이는 Rust compiler에서 금지된 행동이다. 이것이 허용되면 imutable의 의미가 전혀 없어져버리니 금지되어 있다. 그럼 UnsafeCell에서는 어떻게 캐스팅을 하는걸까?

 

[3]

답은 바로 #[lang = "unsafe_cell"] 라는 키워드다. 이 키워드를 컴파일러에게 전달해주어, 원래는 금지된 immputable reference -> mutable reference의 캐스팅을 한다. 이 과정을 약간 더 자세히 알고 싶다면, 이 포스팅을 읽어보자.

 

정리를 하자면, Cell은 그 자체로 interior mutability 제공하지는 못하고, UnsafeCell에서 이를 제공한다. UnsafeCell은 특별한 테크닉을 사용하는 것이 아니라, Rust 언어에서 정의된 키워드를 통해 컴파일러의 제약을 해제하여 interior mutablilby를 제공한다.

 

RefCell 또한 유사하게 UnsafeCell을 사용하는데 이쪽도 살펴보자.

 

[4]

// Positive values represent the number of `Ref` active. Negative values
// represent the number of `RefMut` active. Multiple `RefMut`s can only be
// active at a time if they refer to distinct, nonoverlapping components of a
// `RefCell` (e.g., different ranges of a slice).
//
// `Ref` and `RefMut` are both two words in size, and so there will likely never
// be enough `Ref`s or `RefMut`s in existence to overflow half of the `usize`
// range. Thus, a `BorrowFlag` will probably never overflow or underflow.
// However, this is not a guarantee, as a pathological program could repeatedly
// create and then mem::forget `Ref`s or `RefMut`s. Thus, all code must
// explicitly check for overflow and underflow in order to avoid unsafety, or at
// least behave correctly in the event that overflow or underflow happens (e.g.,
// see BorrowRef::new).
type BorrowFlag = isize;
const UNUSED: BorrowFlag = 0;


/// A mutable memory location with dynamically checked borrow rules
///
/// See the [module-level documentation](index.html) for more.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct RefCell<T: ?Sized> {
    borrow: Cell<BorrowFlag>,
    value: UnsafeCell<T>,
}

RefCell도 Cell과 마찬가지로 값을 UnsafeCell로 감싸고 있지만, 추가적으로 borrow라는 Cell<BorrowFlag>타입의 멤버가 하나 더 있다. 얘가 어떤 역할을 하는지는 RefCell의 public method인 try_borrow_mut()를 통해 알아보자.

impl<T: ?Sized> RefCell<T> {
...
    pub fn try_borrow_mut(&self) -> Result<RefMut<'_, T>, BorrowMutError> {
        match BorrowRefMut::new(&self.borrow) {
            // SAFETY: `BorrowRef` guarantees unique access.
            Some(b) => Ok(RefMut { value: unsafe { &mut *self.value.get() }, borrow: b }),
            None => Err(BorrowMutError { _private: () }),
        }
    }
...
}

struct BorrowRefMut<'b> {
    borrow: &'b Cell<BorrowFlag>,
}

impl<'b> BorrowRefMut<'b> {
...
    fn new(borrow: &'b Cell<BorrowFlag>) -> Option<BorrowRefMut<'b>> {
        // NOTE: Unlike BorrowRefMut::clone, new is called to create the initial
        // mutable reference, and so there must currently be no existing
        // references. Thus, while clone increments the mutable refcount, here
        // we explicitly only allow going from UNUSED to UNUSED - 1.
        match borrow.get() {
            UNUSED => {
                borrow.set(UNUSED - 1);
                Some(BorrowRefMut { borrow })
            }
            _ => None,
        }
    }
...
}

try_borrow_mut()는 위에서 살펴본 UnsafeCell의 get()을 통해 *const T를 받아 온 뒤 &mut T로 변환시켜서 리턴해준다. 이 과정에서 BorrowFlag를 체크한다. BorrowFlag는 0으로 초기화되어있고, 0이면 -1로 변경한 뒤에 &mut T를 리턴, 0이 아니면 에러를 리턴하는 심플한 로직이다. 

impl Drop for BorrowRefMut<'_> {
    #[inline]
    fn drop(&mut self) {
        let borrow = self.borrow.get();
        debug_assert!(is_writing(borrow));
        self.borrow.set(borrow + 1);
    }
}

try_borrow_mut()를 통해 리턴된 mutable이 scope 밖으로 나가면 자동으로 Drop trait의 drop()메소드를 호출한다. 여기에서 다시 BorrowFlag를 리셋한다. 즉 RefCell은 현재의 값이 mutable하게 borrow되었는지 내부적으로 체크하는 flag를 가지고 있고, 이를 통해 런타임에 mutablility aliasing을 체크한다.

 

 

참고

doc.rust-lang.org/nomicon/coercions.html

 

Coercions - The Rustonomicon

Types can implicitly be coerced to change in certain contexts. These changes are generally just weakening of types, largely focused around pointers and lifetimes. They mostly exist to make Rust "just work" in more cases, and are largely harmless. Here's al

doc.rust-lang.org

stackoverflow.com/questions/33233003/how-does-the-rust-compiler-know-cell-has-internal-mutability

 

How does the Rust compiler know `Cell` has internal mutability?

Consider the following code (Playground version): use std::cell::Cell; struct Foo(u32); #[derive(Clone, Copy)] struct FooRef<'a>(&'a Foo); // the body of these functions don't matter fn

stackoverflow.com

ricardomartins.cc/2016/07/11/interior-mutability-behind-the-curtain

 

Interior mutability in Rust, part 3: behind the curtain

Interior mutability in Rust, part 3: behind the curtain 11 Jul 2016 Key takeaways UnsafeCell is the keystone for building interior mutability types (Cell, RefCell, RwLock and Mutex) UnsafeCell wraps around a value and provides a raw mutable pointer to it I

ricardomartins.cc

 

'Computer Science > Rust' 카테고리의 다른 글

Unsafe Rust  (0) 2020.09.27
Rust의 trait object  (0) 2020.08.02
Rust의 async/await와 Future  (0) 2020.07.21
Rust의 Copy trait와 Clone trait  (2) 2020.06.30
Rust의 스마트 포인터  (0) 2020.05.20

+ Recent posts