프로그래머들이 가비지 컬렉션 없이 메모리 안전성을 보장하는 러스트(Rust)의 핵심 기능인 '소유권(Ownership)'을 배울 때 겪는 어려움을 분석하고, 이를 해결하기 위한 체계적인 교육법 설계 과정을 다룬 논문입니다. 개발자들이 흔히 겪는 오개념을 파악하고, 이를 바탕으로 권한(Permissions) 기반의 새로운 개념적 모델을 만들어 교육에 적용한 결과 학습자들의 이해도가 약 9% 향상되었습니다.
번역된 본문
서론
배경
소유권을 위한 개념 목록
소유권을 위한 개념적 모델
평가
관련 연구
일반적인 논의
감사의 말
최초 게재
참고문헌
각주
초록
러스트(Rust)를 배우는 프로그래머들은 가비지 컬렉션 없이 메모리 안전을 보장하는 핵심 메커니즘인 소유권(Ownership) 타입을 이해하는 데 어려움을 겪습니다. 본 논문은 소유권 타입에 대한 교육법을 체계적으로 설계하는 과정을 설명합니다. 첫째, 우리는 러스트 개발자들의 소유권에 대한 오개념을 연구하여 개인의 소유권 지식을 측정하는 새로운 도구인 '소유권 인벤토리(Ownership Inventory)'를 만들었습니다. 우리는 러스트 학습자들이 타입이 잘못된 프로그램이 왜 정의되지 않은 동작을 일으키는지(혹은 일으키지 않는지) 결정하는 것과 같이 러스트의 정적 및 동적 의미론을 연결하지 못한다는 것을 발견했습니다. 둘째, 메모리 경로에 대한 흐름에 민감한(flow-sensitive) 권한 측면에서 대여 검사(borrow checking)를 설명하는 러스트 의미론의 개념적 모델을 만들었습니다. 셋째, 이 모델을 바탕으로 프로그램을 시각화하는 러스트 컴파일러 플러그인을 구현했습니다. 넷째, 인기 있는 러스트 교재인 'The Rust Programming Language'를 위해 새로운 소유권 챕터를 집필하여 권한 모델과 시각화를 더 광범위한 소유권 교육법에 통합했습니다. 다섯째, 학습자들의 소유권 인벤토리 응답을 비교 지표로 사용하여 새로운 교육법의 초기 도입 결과를 기존 버전과 비교 평가했습니다. 지금까지 새로운 교육법은 소유권 인벤토리에서 학습자 점수를 평균 9% 향상시켰습니다(N=342, d=0.56).
서론
소유권은 데이터의 별칭(aliasing)과 변경(mutation)을 관리하기 위한 프로그래밍 규율로, 소유권 타입을 통해 정적으로 강제됩니다. 소유권을 대표하는 프로그래밍 언어인 러스트는 프로그래머가 가비지 컬렉션 없이 메모리 안전 코드를 작성할 수 있게 해줍니다. 러스트의 소유권 모델은 선형 논리, 클래스 기반 별칭 관리, 영역 기반 메모리 관리와 같은 프로그래밍 언어 연구의 여러 아이디어를 종합합니다. 개발자들은 C나 C++와 같은 언어를 사용하여 대규모의 메모리 안전 시스템을 구축할 수 없으므로 소프트웨어 산업은 러스트로 눈을 돌리고 있습니다. 예를 들어, 구글의 안드로이드 팀은 지금까지 150만 줄의 러스트 코드에서 메모리 취약점을 0개 발견했습니다. 이러한 기술 이전의 밝은 전망은 예비 사용자, 특히 소유권 개념에 대해 러스트를 가르치는 데 있어 지속적인 장애물을 감추고 있습니다. 지난 4년 동안의 연구에 따르면 러스트 학습자는 소유권 타입 오류를 수정하는 데 어려움을 겪으며, 사용자들은 소유권이 러스트를 배우는 데 있어 가장 큰 장벽 중 하나라고 스스로 보고합니다. 즉, 타입 시스템의 기술적 요소가 발전하려면 인간-컴퓨터 상호작용(Human factors)과 같은 사용자 요소도 그에 상응하는 발전이 필요합니다.
우리의 연구는 '소유권 타입에 대한 교육법을 어떻게 체계적으로 설계할 수 있는가?'라는 질문으로 시작했습니다. 오늘날 고급 타입 시스템에 대한 대중적인 교육법은 사람들이 어떻게 배우는지에 대한 전문가의 직관과 타입 시스템을 이해하기 어렵게 만드는 요소에 대한 직관에 의해 주도됩니다. 과학자로서 우리는 과학적 원칙을 통해 교육 설계에 접근하고자 했습니다. 즉, 러스트 학습자들의 고충에 대한 관찰을 바탕으로 교육법을 기반을 두고, 학습 성과에 미치는 영향으로 교육법을 평가하고자 했습니다.
본 논문은 이러한 원칙을 실제에 적용한 방법을 설명합니다. 우리는 러스트 학습자들이 가지고 있는 소유권 타입에 대한 오개념을 식별하기 위해 형성적 사용자 연구를 수행했습니다. StackOverflow에 자주 보고되는 러스트 문제에서 과제를 추출하여 소유권에 대한 이해도를 평가하는 새로운 도구인 '소유권 인벤토리(Ownership Inventory)'를 설계했습니다. 우리는 소유권 인벤토리 문제를 해결하려는 36명의 러스트 학습자(N=36)를 연구했습니다. 우리는 학습자들이 일반적으로 프로그램이 소유권 측면에서 왜 타입이 잘못되었는지에 대한 표면적인 이유를 식별할 수 있다는 것을 발견했습니다. 그러나 학습자들은 타입이 잘못된 프로그램이 실행될 경우 (만약 있다면) 어떤 정의되지 않은 동작이 발생할지 이해하지 못합니다. 이러한 오해는 소유권 오류를 수정하는 데 사용되는 비효율적이고 잘못된 전략에 반영되어 있습니다.
Introduction Background A Concept Inventory for Ownership A Conceptual Model for Ownership Evaluation Related Work General Discussion Acknowledgments Originally Published References Footnotes Abstract Programmers learning Rust struggle to understand ownership types, Rust’s core mechanism for ensuring memory safety without garbage collection. This paper describes our process of systematically designing a pedagogy for ownership types. First, we studied Rust developers’ misconceptions of ownership to create the Ownership Inventory, a new instrument for measuring a person’s knowledge of ownership. We found that Rust learners could not connect Rust’s static and dynamic semantics, such as determining why an ill-typed program would (or would not) exhibit undefined behavior. Second, we created a conceptual model of Rust’s semantics that explains borrow checking in terms of flow-sensitive permissions on paths into memory. Third, we implemented a Rust compiler plug-in that visualizes programs under the model. Fourth, we integrated the permissions model and visualizations into a broader pedagogy of ownership by writing a new ownership chapter for The Rust Programming Language , a popular Rust textbook. Fifth, we evaluated an initial deployment of our pedagogy against the original version, using reader responses to the Ownership Inventory as a point of comparison. Thus far, the new pedagogy has improved learner scores on the Ownership Inventory by an average of 9% (N= 342, d= 0.56). 1. Introduction Ownership is a programming discipline for managing the aliasing and mutation of data, enforced statically through ownership types. The flagship programming language for ownership is Rust, which empowers programmers to write memory-safe code without garbage collection. Rust’s ownership model synthesizes several ideas from programming language research, such as linear logic, 9 class-based alias management, 6 and region-based memory management. 10 Developers cannot use languages like C and C++ to build memory-safe systems at scale, 19 so the software industry is turning toward Rust. For example, Google’s Android team has thus far found zero memory vulnerabilities in 1.5 million lines of Rust code. 24 This rosy picture of tech transfer belies a persistent obstacle: teaching Rust to prospective users, especially about ownership. Over the last four years, studies have found that Rust learners struggle to fix ownership type errors, 28 and users self-report that ownership is among their biggest barriers to learning Rust. 23 To wit: Advances in the technical factors of type systems require commensurate advances in the human factors of type systems. Our work started with the question: How can we systematically design a pedagogy for ownership types? Today, popular pedagogies for advanced type systems are driven by experts’ intuitions about how people learn, as well as by intuitions about what makes type systems difficult to understand. As practicing (computer) scientists, we wanted to approach pedagogic design through scientific principles: grounding the pedagogy in observations about the struggles of Rust learners, and then evaluating the pedagogy by its effects on learning outcomes. This paper describes how we put these principles into practice: We ran a formative user study to identify misconceptions about ownership types held by Rust learners ( Section 3 ). We designed a new instrument for evaluating understanding of ownership, the Ownership Inventory, by drawing tasks from commonly reported Rust issues on StackOverflow. We studied N = 36 Rust learners trying to solve Ownership Inventory problems. We found that learners can generally identify the surface-level reason for why a program is ill-typed with respect to ownership. However, learners do not understand what undefined behavior (if any) would occur if an ill-typed program were executed. This misunderstanding is reflected in inefficient and incorrect strategies used to fix ownership errors. We developed a conceptual model of ownership types to address these misconceptions ( Section 4 ). The conceptual model represents the aspects of Rust’s static and dynamic semantics that are relevant to ownership while abstracting other details. The model provides learners a foundation to understand essential concepts such as undefined behavior and the incompleteness of Rust’s ownership type-checker, or “borrow checker.” We implemented tools to execute Rust programs under the conceptual model, generating traces which we visualize to illustrate how the model applies to concrete examples. We wrote a new chapter on ownership for a popular Rust textbook, The Rust Programming Language ( TRPL ), 12 using this conceptual model with these visualizations. We A/B tested our pedagogy against the TRPL baseline ( Section 5 ). We set up and advertised a public website that hosts our TRPL fork. We measured learning outcomes with two kinds of quizzes: simpler comprehension questions about the conceptual model, and more difficult multiple-choice versions of the Ownership Inventory. Learners could correctly answer comprehension questions with 72% accuracy. Our initial deployment improved the average Inventory score from 48% to 57% ( N = 342, p < 0.001, d = 0.56). 2. Background If you, the reader, are not familiar with Rust or ownership, then it’s important to understand the basics to understand this paper’s contributions. One contribution is exactly an explanation of the basics of ownership. We will therefore give a brief exposition of Rust in the form of a heavily abbreviated version of the actual pedagogy we developed for Rust learners. Later sections will refer back to explain the design rationale of the concepts and diagrams mentioned here. 2.1 Ownership. Rust is a systems programming language, most similar in its aims and scope to C++. Rust contains many standard imperative language features, such as mutable variables, conditionals, arrays, loops, structs, and functions. Rust manages memory through a system of compile-time checks known as ownership , as opposed to runtime checks (garbage collection) or manual checks (malloc/free). The basic idea is that heap allocations are owned by a variable, and allocations are freed when their owner goes out of scope, unless the owner moves ownership to another variable. This concept is illustrated in Figure 1 . The goal of ownership is to ensure memory safety, or more generally, to prevent undefined behavior . Undefined behavior means runtime operations without semantics under the Rust language specification, such as dereferencing a null pointer or pretending a string is a boolean. To prevent undefined behavior, the Rust compiler enforces several rules. First, data always has exactly one owner. Second, data is only deallocated on behalf of its owner. Third, data can only be accessed through its owner (except through references, discussed next). These rules prevent situations such as double-frees or use after-frees as shown in Figure 2 . 2.2 Borrowing. To allow accessing data without owning it, Rust provides non-owning pointers called references. References borrow data owned by another variable. Those references can be either immutable (created by & ) or mutable (created by &mut ). Figure 3 shows an example of creating an immutable reference to a piece of a string, and creating a mutable reference to pass to a helper function. Rust enforces safety with references through the concept of permissions. Permissions describe the kinds of operations one can do to data; they come and go based on which owners and references are in use at any given time. Specifically, Rust tracks three permission: readable ( R ), writable ( W ), or ownable ( O ). Figure 4 shows one example of how permissions change over the course of a program. Each operation expects certain permissions from its inputs. For example, getting the length of a vector requires R . Pushing an element to a vector requires W . Deallocating a vector requ