literal과 variable, value, const, static

litral과 변수

Rust에서 리터럴(literal)은 소스 코드에 직접 값을 표현한 것이며(예: 5, “hello”, true), 변수(variable)는 프로그램 실행 중에 값을 저장하고 변경할 수 있는 이름이 있는 저장 공간입니다(예: let x = 5;).

1. 리터럴과 변수

가. 리터럴(Literals)

프로그래밍에서 리터럴은 변수(variable)나 상수(const)와 같이 특정 데이터 유형의 값을 직접 나타내는 표현 방식입니다. 예를 들어, 숫자 10, 문자 ‘a’, 문자열 “hello” , true, false 등이 모두 리터럴입니다. 

  • 직접적인 값:리터럴은 프로그램 실행 중에 변경될 수 없는 고정된 값 자체를 의미합니다. 
  • 불변성: 리터럴은 정의된 이후에 그 값을 변경할 수 없습니다.
  • 표현식:때로는 코드에서 특정 값을 계산하는 표현식 자체가 리터럴로 사용되기도 합니다. 예를 들어, (2 + 3)과 같이 계산 결과가 고정된 값으로 나타나는 경우. 

나. 변수(Variables)

  • 이름이 있는 저장소: 변수는 메모리에 값을 저장하는 이름이 붙은 공간입니다.
  • 가변성 또는 불변성: Rust에서는 let x = 5;처럼 불변 변수도 만들 수 있고, let mut x = 5;처럼 가변 변수도 만들 수 있습니다. 가변 변수는 값을 변경할 수 있습니다.
  • 모든 타입 가능: 변수는 기본 타입은 물론, 구조체나 참조 등 다양한 데이터 타입의 값을 저장할 수 있습니다.
  • 데이터 저장과 조작: 변수는 프로그램에서 필요에 따라 데이터를 저장하거나 변경하는 데 사용됩니다.

다. 비교표

항목리터럴(Literal)변수(Variable)
표현 방식값을 직접 표현이름이 있는 저장 공간
변경 가능 여부변경 불가 (immutable)변경 가능 또는 불가능 (mutable / immutable)
※ Rust에서는 기본값이 immutable
저장 방식변수처럼 메모리에 저장되지 않음메모리에 저장됨
사용 목적상수값(변하지 않는 값) 표현데이터 저장 및 조작
예시5, “hello”, truelet x = 5;에서 x
let mut y = “hello”;에서 y

라. 예제 1

fn main() {
let x = 10; // 10은 정수 리터럴
let y = "hello"; // "hello"는 문자열 리터럴

println!("x = {}", x);
println!("y = {}", y);
}

여기서:

  • 10은 숫자 리터럴 (미리 정의된 값)
  • “hello”는 문자열 리터럴 (미리 코드에 써 넣은 값)

즉, 이런 리터럴 값은 코드에서 바로 보이고 바뀌지 않는 상수 같은 값입니다.


마. 예제 2

fn main() {
let a = 5; // 5는 리터럴
let b = a + 3; // 3도 리터럴, b는 변수

println!("b = {}", b); // 출력: b = 8
}
  • 여기서 5와 3은 literal 또는 literal value (미리 정해진 값)로 바뀔 수 없는데,
  • a와 b는 변수로 나중에 바뀔 수 있습니다. 그러나, Rust에서는 mut가 없으면 불변이 기본임

바. 요약

Rust에서 10, “hello”, true 같은 값들은 literal로서
코드에 직접 써 넣은, 미리 정의된 값들입니다.

2. 리터럴과 값(value)

가. Literal (리터럴)

“소스 코드에 직접 쓰인 값”
즉, 고정된 형태의 값을 코드에 명시한 것입니다.

42       // 정수 리터럴
3.14 // 부동소수점 리터럴
"hello" // 문자열 리터럴
true // 불리언 리터럴
'b' // 문자 리터럴

리터럴은 컴파일 타임에 결정되며 변하지 않습니다.


나. Value (값)

프로그램이 실행될 때 메모리에 존재하는 데이터입니다.
리터럴, 변수, 연산 결과 등 다양한 방식으로 생성될 수 있습니다.

let x = 5;            // 리터럴 5를 변수 x에 저장 → value는 5
let y = x + 2; // 연산 결과 7도 value
let s = String::from("hi"); // value는 "hi"라는 힙에 저장된 문자열

value는 runtime의 개념이며, 리터럴은 value를 만들기 위한 한 방법입니다.


다. 비교표

항목LiteralValue
정의코드에 직접 적힌 고정 값실행 중 사용되는 데이터
예시“hello”, 3.14, true변수에 저장된 값, 함수의 반환값 등
생성 시점컴파일 타임런타임
변경 가능성불변변경 가능 (가변 변수에 저장된 경우)
위치코드에 직접 작성됨메모리에 존재

라. 관계 요약

  • 리터럴은 value를 만드는 수단 중 하나입니다.
  • 모든 리터럴은 value지만, 모든 value가 리터럴은 아닙니다.

예:

let a = 1 + 2;  
// 1과 2는 리터럴 → 3은 value (리터럴이지만 연산 결과이기도 함)

let b = a * 2;
// b의 값 6은 value지만 literal은 아님 (런타임 연산 결과)

3. literal과 const, static

구분의미특징
literal코드에 직접 써 넣은 값미리 정의된 값. 변수나 상수에 저장 가능
const컴파일 시점 상수항상 값이 고정, 함수 밖/안 모두 사용 가능, 메모리 없음
static정적 변수 (전역 상수)프로그램 전체에서 공유, 고정 메모리 공간 사용

가. 예제

const CONST_VALUE: i32 = 10;
static STATIC_VALUE: i32 = 20;

fn main() {
let literal = 5; // 5는 literal

println!("literal: {}", literal);
println!("const: {}", CONST_VALUE);
println!("static: {}", STATIC_VALUE);
}

나. 설명

  1. 5는리터럴(literal)
    → 그냥 코드에 직접 쓴 고정된 값
  2. CONST_VALUE는 const
    → 컴파일 시간에 값이 확정됨. 메모리에 저장되지 않음.
    → const는 let처럼 지역적으로도 가능하지만 항상 값이 바뀌지 않음
  3. STATIC_VALUE는 static
    → 프로그램이 끝날 때까지 살아있는 고정 메모리 주소에 저장됨
    → 전역 변수처럼 사용 가능
    → 다만static mut은 unsafe하게 써야 함

다. 비교표

항목값이 고정됨변경 가능성메모리 위치사용 시기
literal스택 또는 인라인코드 내 직접
const없음 (값 인라인)컴파일 시
static❌ (기본)전역 메모리런타임 전체 기간

라. static mut의 대체 수단

static mut COUNTER: i32 = 0;

fn main() {
unsafe {
COUNTER += 1;
println!("counter: {}", COUNTER);
}
}
  • static mut은 동시성 문제 때문에 unsafe로 접근해야 함

Rust의const와 static은 값이 컴파일 타임에 반드시 결정되어야 하는데,

다음처럼 런타임 정보나 복잡한 로직으로 초기화해야 하는 경우에는 문제가 생깁니다:

static MY_STRING: String = String::from("hello"); // ❌ 에러!

🚫String은 컴파일 타임에 생성할 수 없기 때문에 static으로 직접 초기화 불가능

이럴 때 쓰는 게 바로 아래 세 가지입니다:

(1) const fn

  • const fn은 컴파일 타임에 실행 가능한 함수를 정의합니다.
  • const 상수나 const 표현식에서 사용할 수 있음
const fn square(x: i32) -> i32 {
x * x
}

const RESULT: i32 = square(4); // ✅ OK!

📌 단점: const fn은 제한이 많아서, String, Vec, 파일읽기 등은 못 씀


(2) lazy_static (deprecated 경향 있음)

  • 복잡한 값도 런타임 최초 1회 초기화 후 전역처럼 사용 가능
  • macro_use가 필요하고, 내부적으로 unsafe와 Mutex를 씀
#[macro_use]
extern crate lazy_static;

use std::collections::HashMap;

lazy_static! {
static ref CONFIG: HashMap<&'static str, i32> = {
let mut m = HashMap::new();
m.insert("threshold", 10);
m
};
}

fn main() {
println!("threshold = {}", CONFIG["threshold"]);
}

✅ 장점: 복잡한 초기화 가능
⚠️ 단점: 오래된 방식이고 macro 기반, 무거움


(3) once_cell (추천)

  • 최신 Rust 커뮤니티에서는 lazy_static 대신 once_cell을 많이 씁니다.
  • 런타임 최초 1회 초기화, macro 없이, 더 가볍고 안전
use once_cell::sync::Lazy;
use std::collections::HashMap;

static CONFIG: Lazy<HashMap<&'static str, i32>> = Lazy::new(|| {
let mut m = HashMap::new();
m.insert("threshold", 10);
m
});

fn main() {
println!("threshold = {}", CONFIG["threshold"]);
}

✅ 장점: lazy_static보다 깔끔하고 안전
🔒 sync::Lazy: 여러 스레드에서 안전하게 사용 가능
🧵 unsync::Lazy: 단일 스레드용으로 더 빠름


(4) const, static 등 비교표

방식초기화 시점복잡한 타입 가능스레드 안전성비고
const컴파일 타임❌ (기본 타입만)N/A빠르고 간단
static컴파일 타임❌ (기본 타입만)가능전역 메모리 사용
const fn컴파일 타임제한적가능복잡한 초기화 불가능
lazy_static런타임 (1회만)무겁고 오래된 방식
once_cell런타임 (1회만)✅ or ❌최신 방식, 매크로 없이 가능

(5) 요약

  • 간단한 상수는 const
  • 전역 정적 값은 static
  • 복잡한 초기화가 필요한 전역 값은 once_cell::Lazy 추천

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다