녹에서 글로벌 변수를 선언하고 사용하는 것은 까다로울 수 있습니다. 일반적 으로이 언어의 경우 Rust는 우리가 매우 명시 적으로 강요함으로써 견고성을 보장합니다.
이 기사에서는 녹 컴파일러가 우리를 저장하려는 함정에 대해 논의 할 것입니다. 그런 다음 다양한 시나리오에 사용할 수있는 최고의 솔루션을 보여 드리겠습니다.
키 테이크 아웃
글로벌 범위에서 허용되지 않기 때문에`static` 또는`const '를 사용하여 글로벌 변수를 녹에서 선언합니다.
스레드-안전 런타임 글로벌 변수의 초기화의 경우`std :: sync :: evenc '또는`lazy_static` 또는`even_cell`와 같은 외부 라이브러리 사용을 고려하십시오.
잠재적 안전 문제로 인해 '정적 뮤트'를 직접 사용하지 마십시오. 대신, '안전하지 않은'블록 내에서 액세스를 랩핑하거나 뮤트와 같은 동기화 프리미티브를 사용하십시오.
단일 스레드 애플리케이션의 경우``Thread_Local!`매크로는 동기화 없이도 글로벌 상태를 처리 할 수있는 안전한 방법을 제공 할 수 있습니다.
공유 소유권 및 스레드 안전을 위해`arc`와 같은 스마트 포인터를 사용하여 가능한 경우 로컬 범위로 전 세계 변수를 Refactor.
Rust에서 'const'와 'static`의 차이점을 이해하십시오.'const '변수는 감소하고 불변이지만'정적 '변수는 원자 유형이나 뮤트와 같은 내부 돌연변이 옵션을 사용하여 돌연변이 가능한 상태를 가질 수 있습니다.
개요
Rust에서 글로벌 상태를 구현하기위한 많은 옵션이 있습니다. 서두르면 여기 내 추천에 대한 간단한 개요가 있습니다.
다음 링크를 통해이 기사의 특정 섹션으로 이동할 수 있습니다.
<: :> globals : arc / rc 컴파일 타임 초기화 된 글로벌 : const t / static t
쉬운 런타임 초기화를 위해 외부 라이브러리를 사용하여 Globals : Lazy_static / One_Cell
자신의 런타임 초기화를 구현하십시오
단일 스레드 런타임 초기화를위한 특수 사례 : Thread_local
Rust에서 글로벌 변수를 사용하려는 순진한 첫 번째 시도
글로벌 변수를 사용하지 않는 방법의 예부터 시작하겠습니다. 프로그램의 시작 시간을 글로벌 문자열로 저장하고 싶다고 가정하십시오. 나중에 여러 스레드에서 값에 액세스하고 싶습니다.
Rust 초보자는 Let를 사용하여 Rust의 다른 변수와 똑같은 글로벌 변수를 선언하려는 유혹을받을 수 있습니다. 그런 다음 전체 프로그램은 다음과 같이 보일 수 있습니다
-
놀이터에서 직접 시도해보십시오!
정적으로 다시 시도해 봅시다 :
컴파일러는 아직 행복하지 않습니다
hm이므로 정적 변수의 초기화 값은 런타임에 계산할 수 없습니다. 그러면 처음으로 초기화되지 않도록하자?
이것은 새로운 오류를 산출합니다 :
그래서 그게 작동하지 않습니다! 모든 정적 값은 사용자 코드가 실행되기 전에 완전히 초기화되고 유효해야합니다.
JavaScript 또는 Python과 같은 다른 언어에서 녹슬 으면 불필요하게 제한적으로 보일 수 있습니다. 그러나 모든 C Guru는 정적 초기화 순서 치실에 대한 이야기를 들려 줄 수 있으며, 이는 조심하지 않으면 정의되지 않은 초기화 순서로 이어질 수 있습니다.
예를 들어, : 와 같은 것을 상상하십시오
이 코드 스 니펫에서는 원형 의존성으로 인해 안전한 초기화 순서가 없습니다.
안전에 신경 쓰지 않는 C 인 경우 결과는 A : 1 B : 1 C : 2입니다. 코드가 실행되기 전에 초기화되지 않으면 순서가 상단에서 바닥에서 정의됩니다. 각 컴파일 장치 내에서. 최소한 결과가 무엇인지 정의했습니다. 그러나 "Fiasco"는 정적 변수가 다른 .CPP 파일과 다른 컴파일 장치에서 시작될 때 시작됩니다. 그런 다음 순서는 정의되지 않으며 일반적으로 컴파일 명령 줄의 파일 순서에 따라 다릅니다.
녹에서는 제로 이니티브 화가 일이 아닙니다. 결국, 0은 상자와 같은 많은 유형에 대해 잘못된 값입니다. 또한 Rust에서는 이상한 주문 문제를 받아들이지 않습니다. 안전하지 않은 상태에서 멀리 떨어져있는 한 컴파일러는 제정신 코드 만 쓸 수 있어야합니다. 그렇기 때문에 컴파일러가 간단한 런타임 초기화를 사용하지 못하게합니다.
그러나 널 포인터와 동등한 NONE을 사용하여 초기화를 우회 할 수 있습니까? 적어도 이것은 모두 녹 타입 시스템에 따른 것입니다. 분명히 나는 초기화를 기본 함수의 맨 위로 옮길 수 있습니까?
아, 글쎄, 우리가 얻는 오류는… 입니다
이 시점에서 안전하지 않은 {...} 블록으로 감을 수 있으면 작동합니다. 때로는 유효한 전략입니다. 코드의 나머지 부분이 예상대로 작동하는지 테스트 할 수도 있습니다. 하지만 내가 보여주고 싶은 관용적 해결책은 아닙니다. 컴파일러가 안전하게 보장되는 솔루션을 탐색 해 봅시다.
refactor 예제
이미이 예제에 전역 변수가 전혀 필요하지 않다는 것을 이미 알았을 것입니다. 그리고 글로벌 변수가없는 솔루션을 생각할 수 있다면, 우리는 그것들을 피해야합니다. .
여기서 아이디어는 주요 기능 안에 선언을 배치하는 것입니다.
유일한 문제는 차용 체커입니다
<span>use chrono<span>::</span>Utc;
</span>
<span>let START_TIME = Utc::now().to_string();
</span>
<span>pub fn main() {
</span> <span>let thread_1 = std<span>::thread::</span>spawn(<span>||</span>{
</span> <span>println!("Started {}, called thread 1 {}", START_TIME.as_ref().unwrap(), Utc::now());
</span> <span>});
</span> <span>let thread_2 = std<span>::thread::</span>spawn(<span>||</span>{
</span> <span>println!("Started {}, called thread 2 {}", START_TIME.as_ref().unwrap(), Utc::now());
</span> <span>});
</span>
<span>// Join threads and panic on error to show what went wrong
</span> thread_1<span>.join().unwrap();
</span> thread_2<span>.join().unwrap();
</span><span>}
</span>
로그인 후 복사
로그인 후 복사
로그인 후 복사
이 오류는 명백하지 않습니다. 컴파일러는 스폰 된 스레드가 기본 함수의 스택 프레임에있는 value start_time보다 오래 살 수 있다고 알려줍니다.
기술적으로, 우리는 이것이 불가능하다는 것을 알 수 있습니다. 스레드가 결합되어 하위 스레드가 완료되기 전에 기본 스레드가 종료되지 않습니다.
그러나 컴파일러는이 특정 사례를 파악하기에 충분히 똑똑하지 않습니다. 일반적으로 새 스레드가 생성되면 제공된 폐쇄는 정적 수명으로 항목 만 빌릴 수 있습니다. 다시 말해서, 빌린 가치는 전체 프로그램 수명 동안 살아 있어야합니다.
Rust에 대해 배우는 사람이라면 누구나 글로벌 변수에 연락하고 싶은 시점 일 수 있습니다. 그러나 그보다 훨씬 쉬운 두 가지 솔루션이 있습니다. 가장 간단한 것은 문자열 값을 복제 한 다음 문자열의 소유권을 클로저로 옮기는 것입니다. 물론 추가 할당과 여분의 메모리가 필요합니다. 그러나이 경우 짧은 문자열 일 뿐이며 성능이 중요하지 않습니다.
그러나 공유하기가 훨씬 더 큰 대상이라면 어떻게해야합니까? 복제하지 않으려면 참조 카운트 스마트 포인터 뒤에 싸십시오. RC는 단일 스레드 참조 카운트 유형입니다. Arc는 스레드간에 값을 안전하게 공유 할 수있는 원자 버전입니다. 따라서 컴파일러를 만족시키기 위해 다음과 같이 아크를 사용할 수 있습니다.
놀이터에서 직접 시도해보십시오!
<span>use chrono<span>::</span>Utc;
</span>
<span>let START_TIME = Utc::now().to_string();
</span>
<span>pub fn main() {
</span> <span>let thread_1 = std<span>::thread::</span>spawn(<span>||</span>{
</span> <span>println!("Started {}, called thread 1 {}", START_TIME.as_ref().unwrap(), Utc::now());
</span> <span>});
</span> <span>let thread_2 = std<span>::thread::</span>spawn(<span>||</span>{
</span> <span>println!("Started {}, called thread 2 {}", START_TIME.as_ref().unwrap(), Utc::now());
</span> <span>});
</span>
<span>// Join threads and panic on error to show what went wrong
</span> thread_1<span>.join().unwrap();
</span> thread_2<span>.join().unwrap();
</span><span>}
</span>
로그인 후 복사
로그인 후 복사
로그인 후 복사
이것은 글로벌 변수를 피하면서 스레드 간 상태를 공유하는 방법에 대한 빠른 요약이었습니다. 내가 지금까지 보여준 것 외에도 공유 상태를 수정하려면 내부 돌연변이가 필요할 수도 있습니다. 내부 돌연변이의 전체 범위는이 기사의 범위를 벗어납니다. 그러나이 특정 예에서는 strest_time에 스레드-안전 내부 돌연변이를 추가하기 위해 Arc >를 선택합니다.
글로벌 변수 값이 컴파일 시간에 알려진 경우
내 경험상 글로벌 상태의 가장 일반적인 사용 사례는 변수가 아니라 상수입니다. 녹에서, 그들은 두 가지 맛으로옵니다 :
const로 정의 된 상수 값. 이것들은 컴파일러에 의해 상거됩니다. 내부 돌연변이는 허용되지 않습니다.
둘 다 컴파일 타임 상수로 초기화 될 수 있습니다. 이것들은 42 또는 "Hello World"와 같은 단순한 값 일 수 있습니다. 또는 Const로 표시된 여러 다른 컴파일 타임 상수 및 기능을 포함하는 표현 일 수 있습니다. 원형 의존성을 피하는 한. (Rust Reference에서 지속적인 표현에 대한 자세한 내용은 찾을 수 있습니다.)
일반적으로, 내부 돌연변이가 필요하지 않거나 구체적으로 인라인을 피하려고하지 않는 한, const는 더 나은 선택입니다. .
내부 돌연변이가 필요한 경우 몇 가지 옵션이 있습니다. 대부분의 프리미티브의 경우 std :: sync :: atomic에는 해당 원자 변형이 있습니다. 그들은 원자 적으로 값을로드, 저장 및 업데이트 할 수있는 깨끗한 API를 제공합니다.
원자가가 없으면 일반적인 선택은 잠금입니다. Rust의 표준 라이브러리는 RWLOCK (Read Write Lock) 및 상호 제외 잠금 (MUTEX)을 제공합니다.
그러나 런타임시 값을 계산해야하거나 힙 할당이 필요한 경우 const와 static은 도움이되지 않습니다.
런타임 초기화와 함께 녹에서 단일 스레드 글로벌 변수
내가 쓰는 대부분의 응용 프로그램에는 단일 스레드 만 있습니다. 이 경우 잠금 장치가 필요하지 않습니다
그러나 스레드가 하나만 있기 때문에 STATIC MUT를 직접 사용하지 말고 안전하지 않은 상태에서 액세스를 랩핑해야합니다. 이런 식으로, 우리는 심각한 기억 부패로 끝날 수 있습니다.
예를 들어 전역 변수에서 불안한 차용은 동시에 여러 개의 변이 가능한 참조를 제공 할 수 있습니다. 그런 다음 그중 하나를 사용하여 벡터를 반복하고 다른 하나는 동일한 벡터에서 값을 제거 할 수 있습니다. 그런 다음 반복기는 유효한 메모리 경계를 넘어서 안전한 녹이 막을 수있는 잠재적 인 충돌을 넘어 설 수 있습니다.
그러나 표준 라이브러리는 단일 스레드 내에서 안전한 액세스를 위해 "전역으로"저장 값을 저장하는 방법이 있습니다. 나는 스레드 현지인에 대해 이야기하고 있습니다. 많은 스레드가 있으면 각 스레드는 변수의 독립적 인 사본을 가져옵니다. 그러나 우리의 경우 단일 스레드가있는 경우 사본이 하나뿐입니다.
스레드 로컬은 Thread_local과 함께 생성됩니다! 매크로. 다음 예에서 볼 수 있듯이 액세스하려면 폐쇄를 사용해야합니다.
모든 솔루션 중 가장 간단한 것은 아닙니다. 그러나 우리는 임의의 초기화 코드를 수행 할 수 있으며, 이는 값에 대한 첫 번째 액세스가 발생할 때 바로 그 시간에 실행됩니다.
스레드-로컬은 내부 돌연변이와 관련하여 정말 좋습니다. 다른 모든 솔루션과 달리 동기화가 필요하지 않습니다. 이를 통해 내부 돌연변이에 refcell을 사용할 수 있습니다. 이는 MUTEX의 잠금 오버 헤드를 피합니다.
스레드-로컬의 절대 성능은 플랫폼에 크게 의존합니다. 그러나 나는 자체 PC에서 잠금 장치에 의존하는 내부 돌연변이와 비교하여 빠른 테스트를 수행했으며 10 배 빠른 것으로 나타났습니다. 나는 어떤 플랫폼에서도 결과가 뒤집힐 것으로 기대하지는 않지만 성능에 대해 깊이 신경 쓰면 자신만의 벤치 마크를 실행해야합니다. .
내부 돌연변이에 refcell을 사용하는 방법의 예는 다음과 같습니다.
놀이터에서 직접 시도해보십시오! = refcell :: new (utc :: now (). to_string ()));
fn main () {
global_data.with (| 텍스트 | {
println! ( "글로벌 문자열은 {}", *text.borrow ());
});
global_data.with (| 텍스트 | {
*text.borrow_mut () = utc :: now (). to_string ();
});
global_data.with (| 텍스트 | {
println! ( "글로벌 문자열은 {}", *text.borrow ());
});
} "width ="100%">
놀이터에서 직접 시도해보십시오!
녹에서 정적과 const는 모두 전역 변수를 선언하는 데 사용되지만 특성이 다릅니다. Rust의 Const는 일정한 값이므로 변경되지 않을 값이라는 것을 의미합니다. 변수와 비슷하지만 그 값은 일정하며 변경할 수 없습니다. 반면, Rust의 정적 변수는 프로그램 바이너리의 읽기 전용 데이터 섹션에 저장된 글로벌 변수입니다. 선언 된 범위뿐만 아니라 전체 프로그램에서 사용할 수있는 변수입니다. const와 달리 정적 변수는 메모리에 고정 된 주소가 있습니다. Rust에서 글로벌 변수를 어떻게 선언 할 수 있습니까?
Rust에서는 정적 키워드를 사용하여 글로벌 변수를 선언 할 수 있습니다. 예는 다음과 같습니다. 정적 글로벌 : i32 = 10;
이 예에서는 글로벌이 유형 I32의 글로벌 변수이며 값 10으로 초기화됩니다. 정적 변수가 읽습니다. 당신은 그것들을 수정할 수 없습니다.
Rust에서 글로벌 변수를 수정할 수 있습니까?
아니요, 기본적으로 불변으로 녹에서 글로벌 변수를 직접 수정할 수는 없습니다. 이것은 컴파일 타임에 데이터 경주를 방지하기위한 Rust의 안전 기능입니다. 그러나 STD :: SYNC 모듈에서 MUTEX 또는 RWLOCK을 사용하여 돌연변이 가능한 글로벌 변수를 달성 할 수 있습니다 Rust의 매크로를 사용하면 정적 변수를 게으른 방식으로 초기화 할 수 있습니다. 이는 정적 변수의 초기화가 처음으로 액세스 할 때까지 지연됨을 의미합니다. 이것은 초기화가 비싸고 필요한 경우에만 유용 할 수 있습니다.
'lazy_static!' 'Lazy_Static!'을 사용하여 Rust에서 글로벌 변수를 선언하는 방법 : extern Crate Lazy_static; Lazy_static! {> 정적 참조 글로벌 : mutex = mutex :: new (0); 이 예에서 글로벌은 유형 mutex 의 글로벌 변수이며 초기화됩니다. 값 0. 'Lazy_static!'은 초기화가 스레드 안전 방식으로 수행되도록합니다. > Rust에서 'static'은 읽기 전용의 전역 변수를 선언하는 데 사용되는 반면 'STATIC MUT'는 변이 가능한 전역 변수를 선언하는 데 사용됩니다. 그러나 두 스레드가 변수에 동시에 액세스하면 정의되지 않은 동작을 유발할 수 있기 때문에 '정적 MUT'는 안전하지 않습니다.
녹에서 '정적 뮤트'변수에 어떻게 안전하게 액세스 할 수 있습니까?
'안전하지 않은'키워드를 사용하여 Rust에서 'STATIC MUT'변수에 안전하게 액세스 할 수 있습니다. 예는 다음과 같습니다.
static mut global : i32 = 10;
fn main () { 안전하지 않은 {
global = 20;
println! ( "{} ", global); }
}
이 예에서는 '안전하지 않은'키워드가 사용하여 코드가 안전 코드에서 정의되지 않은 작업을 수행 할 수 있음을 나타냅니다.
Rust에서 글로벌 변수의 수명은 얼마입니까?
Rust에서 글로벌 변수의 수명은 프로그램의 전체 기간입니다. 즉, 프로그램이 시작될 때 글로벌 변수가 생성되고 프로그램이 종료 될 때 파괴됩니다.
Rust 함수에서 글로벌 변수를 사용할 수 있습니까?
예, Rust에서 글로벌 변수를 사용할 수 있습니다. 기능. 그러나 제대로 사용하지 않으면 데이터 경주로 이어질 수 있으므로 사용할 때주의해야합니다. 일반적으로 가능할 때마다 글로벌 변수 대신 로컬 변수를 사용하는 것이 좋습니다. Rust의 글로벌 변수에 대한 대안은 무엇입니까?
Rust 프로그램의 다른 부분간에 데이터를 공유하려면 글로벌 변수에 대한 몇 가지 대안을 사용할 수 있습니다. 여기에는 기능 인수로서 데이터를 전달하고, 함수에서 데이터를 반환하고, 스트러크 및 열거와 같은 데이터 구조를 사용하고, 채널 및 잠금과 같은 동시성 프리미티브를 사용하는 것이 포함됩니다.
위 내용은 녹에서 글로벌 변수를 특이 적으로 사용하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!