`t6`의 크기를 컴파일 타임에 알 수 없어서 컴파일에 실패 했습니다. 왜냐하면 str 타입에 대해서 Sized trait 이 구현되어 있지 않거든요. 함수 정의 쪽에 ?Sized를 추가해 보세요.
Rust는 “Zero 비용 추상화“를 지향하기 때문에 이를 지원하기 위한 수단으로 컴파일 시간에 모든 데이터의 크기를 “알고” 있는 것을 전제로 한다. 그런데 &str 같은 슬라이스를 print_item(&T) 함수에 넘기면 T가 str이 되므로 원래 할당된 공간의 "hello"를 가리키게 된다. 현재는 그 크기가 다섯 글자짜리 문자열이지만, 추후에 받아들이는 문자열의 실제 길이에 따라 데이터의 크기가 변할 수 있는 상황이 되는 것이다.
이 경우에는 컴파일 오류에서 안내된 것 처럼 ?Sized를 template의 type에 추가해서 type 확인에 대한 제한을 완화해 주면 해당 오류가 해결되면서 str type의 함수 template도 잘 동작한다.
대부분의 경우에는 Rust compiler가 암묵적으로 Sized Marker Trait을 자동으로 붙여주어서 그 크기를 알고 있다고 알린다. 하지만 사용자가 ?Sized를 붙이는 경우에는 크기 확인에 대한 제약을 완화해 주어서 컴파일을 가능하게 해준다. 위 예제의 경우 &str fat pointer를 넘기게 되므로 실제 데이터 “hello”의 크기는 늘거나 줄어들 수 있어도 이것을 가리키고 있는 fat pointer의 크기 자체는 변하지 않으므로 ?Sized 로 조건을 완화해도 문제가 없는 것이다.
다시 말해서, 컴파일 타임에 “모든” 데이터 타입의 크기를 알고 있어야 한다는 것은 Rust에서 엄밀하게 말해 정확하지 않다.
좀 더 정확하게 말하자면:
“스택에 직접 올라가는 값이나 변수의 크기는 컴파일 타임에 반드시 알아야 하지만, 포인터나 참조자로 간접 참조하는 데이터는 그렇지 않아도 된다.”
여기에서 “그렇지 않아도 되는” 조건에 &str template에 해당하는 것이어서 ?Sized로 이것을 풀어 줄 수 있었던 것이다.
Sized / ?Sized Marker Trait
Sized는 Rust 표준 라이브러리(std::marker::Sized)에 정의된 마커 트레이트(marker trait)이다. Sized marker trait은 다음과 같은 특징을 가진다.
메소드가 없음: 실행해야 할 함수가 없고, 컴파일러에게 “이 타입은 크기를 컴파일 타임에 알 수 있다”고 알려주는 표식 역할만 수행.
기본값(Default): Rust에서 제네릭 타입 파라미터를 만들 때, 우리가 따로 적지 않아도 컴파일러는 기본적으로 T: Sized가 붙어 있는 것으로 간주.
이런게 왜 필요할까?
만약, Rust가 Sized / ?Sized 같은 번접한 marker trait을 빼고 “모든 타입의 크기를 몰라도 된다”라고 기본 설정을 했다면 컴파일 타임에 ‘모든’ 데이터 크기를 알고 Zero cost abstraction 정책으로 stack을 중심으로 하는 효율적 데이터 할당을 통한 성능 최적화 추구를 위한 설계 철학 자체가 이뤄질 수 없었을 것이다. 모든 데이터가 동적으로 할당되는 heap에 있었어야 했을 테니 말이다.
스택(Stack): CPU의 스택 포인터 레스트터와 변경하면 되는 매우 빠른 메모리 할당, 데이터의 크기를 미리 알고 있는 경우(Sized가 선언된 경우) 이 영역을 사용할 수 있음.
힙(Heap): 빈 공간을 검색하고, 단편화를 관리하며 운영체제 시스템 콜을 부를 수도 있는 상대적으로 느린 동적 할당.
Rust의 모든 generic이 기본적으로 Sized를 보장 함으로써, 데이터를 스택에 올려 둘 수 있었던 것이고 이것이 Rust의 빠른 성능의 바탕이 된다. 결론적으로 ?Sized는 이 철학을 깨는 것은 아니고 fat pointer의 고정된 크기는 알고 있으니 그것이 가리키고 있는 영역에 대한 검사 조건을 완화 해도 문제가 없다는 표시인 것이다.
외부 모니터가 연결된 상태에서 Disney+에서 영화 목록들은 잘 보이다가 막상 재생을 하려니 검은색 화면이 뜨면서 아무런 동작도 하지 않았다. 이 증상을 검색해 보니 “Chrome을 쓰세요”라는 해결 방법이 많았고 실제로 그렇게 해서 동작이 되기는 했다. 다만 문제는 이 때의 해상도가 무척 낮아 보였는데, Chrome broswer에서 재공해 주는 chrome://media-internals/ 기능에 의하면 재생할 때의 해상도가 854×480 SD 화질이었다. 화질이 낮기는 해도 Chrome에서 되기는 하는 셈이다.
이런 의문이 들었다.
Chrome에서는 왜 화질이 낮은가?
Safari에서는 왜 재생이 안되는가?
Chrome에서 화질이 낮은 이유
Chrome에서는 DRM이 걸린 contents를 재생하기 위해 Widevine을 이용해서 decryption을 수행하는데 이 때, 고화질의 미디어를 모니터가 아닌 곳(파일 같은)으로 빼내는 것을 막기위해서 미디어 컨탠츠 -> 메모리 -> 디스플레이 엔진 -> 케이블 -> 싱크디바이스(모니터)까지의 전과정에서 일반 메모리에 노출 없이 TEE(Trusted Execution Enivrionment)에서 이루어지면 level1으로 분류하고 이 때는 4K UHD까지의 해상도가 지원된다.
반면, TEE의 개입 없이 일반 시스템 메모리에서 decryption과 프레임 처리가 이루어지면 level3로 분류되는데 이 때는 최대 해상도가 720p로 제한된다. 이러한 DRM decryption 환경에 대한 정보는 source와 sink 디바이스간에 연결될 때 초기에 handshake 과정에서 결정 되기에 위에서 말한 디스플레이 경로상에 HDCP를 제대로 지원하지 못하는 문제가 있는 경우 480p와 같은 낮은 해상도로 재생이 이루어지게 된다.
Safari에서는 재생이 안되는 이유
DRM 걸린 컨텐츠를 재생하는 방법들 중에 Safari는 Chrome과 달리 Fairplay를 사용하는데 이 경우 재생 경로에 문제가 있으면 Widevine 처럼 해상도를 낮춰서 라도 재생하는 것이 아니라 아예 재생을 막아 버린다. 그러면 내 경우에는 도대체 뭐가 문제여서 재생을 막아버리는 걸까?
USB3.0 HUB에 연결된 HDMI
나는 HDMI 모니터를 USB3.0 HUB를 통해서 맥북에 연결했는데, 이 경우 영상 신호가 USB로 변환되는 과정을 거쳐야 한다. 이 때 사용되는 DisplayLnik나 Silicon Motion 같은 frame을 greb, 압축해서 전달하는 메커니즘이 개입하게 되면 HDCP가 제대로 동작하지 않는다. 이 때문에 Widevine에서는 화질 저하 Fairplay에서는 재생 불가가 되는 것이다.
결론 및 해결책
가장 간단한 해결책은 HDMI cable을 맥북에 직접 연결하는 것이다. 이 경우 frame을 greb하는 경로가 없어지고, 따라서 Safari에서 Disney+가 재생되지 않는 문제가 해결된다. 또 다른 해결 방법은 frame breb 같은 방법 없이 모니터로 출력하는 DP ALT mode를 지원하는 dock 혹은 HUB 제품을 사용하는 것이다. 실제로 다른 종류의 USB type-C HUB나 HP Thunderbolt Dock G2로 확인해 보니 문제 없이 잘 재생되었다.