구조체 표현식

구조체 표현식

구조체 값을 만드는 표현식을 구조체 표현식(struct expression) 이라고 해요. 구조체뿐 아니라 enum과 union 값도 이 표현식으로 만들어요. 구조체, enum variant, 혹은 union 항목을 가리키는 경로(path) 뒤에 그 항목의 필드 값들을 붙이는 형태예요.

출처: Rust Reference - Struct expressions

본문

StructExpression ->
    PathInExpression { ( StructExprFields | StructBase )? }

StructExprFields ->
    StructExprField ( , StructExprField )* ( , StructBase | ,? )

StructExprField ->
    OuterAttribute*
    (
        IDENTIFIER
      | ( IDENTIFIER | TUPLE_INDEX ) : Expression
    )

StructBase -> .. Expression

구조체 표현식의 예시를 몇 가지 볼게요.

#![allow(unused)]
fn main() {
struct Point { x: f64, y: f64 }
struct NothingInMe { }
mod game { pub struct User<'a> { pub name: &'a str, pub age: u32, pub score: usize } }
enum Enum { Variant {} }
Point {x: 10.0, y: 20.0};
NothingInMe {};
let u = game::User {name: "Joe", age: 35, score: 100_000};
Enum::Variant {};
}

튜플 구조체튜플 enum variant는 보통 값 네임스페이스의 생성자를 참조하는 호출 표현식(call expression) 으로 인스턴스화돼요. 이것은 중괄호를 쓰면서 타입 네임스페이스의 생성자를 참조하는 구조체 표현식과는 구별돼요.

#![allow(unused)]
fn main() {
struct Position(i32, i32, i32);
Position(0, 0, 0);  // 튜플 구조체를 만드는 일반적인 방법.
let c = Position;  // `c`는 인자를 3개 받는 함수예요.
let pos = c(8, 6, 7);  // `Position` 값을 만들어요.

enum Version { Triple(i32, i32, i32) };
Version::Triple(0, 0, 0);
let f = Version::Triple;
let ver = f(8, 6, 7);
}

호출 경로의 마지막 세그먼트는 타입 별칭(type alias) 을 참조할 수 없어요.

#![allow(unused)]
fn main() {
trait Tr { type T; }
impl<T> Tr for T { type T = T; }

struct Tuple();
enum Enum { Tuple() }

// <Unit as Tr>::T(); // 에러 발생 -- `::T`는 타입이지 값이 아니에요.
<Enum as Tr>::T::Tuple(); // OK
}

유닛 구조체유닛 enum variant는 보통 값 네임스페이스의 상수를 참조하는 경로 표현식으로 인스턴스화돼요.

#![allow(unused)]
fn main() {
struct Gamma;
// Gamma 유닛 값. 값 네임스페이스의 상수를 참조해요.
let a = Gamma;
// `a`와 완전히 같은 값이지만, 타입 네임스페이스를 참조하는
// 구조체 표현식으로 만들어진 값이에요.
let b = Gamma {};

enum ColorSpace { Oklch }
let c = ColorSpace::Oklch;
let d = ColorSpace::Oklch {};
}

필드 구조체 표현식

필드를 중괄호로 감싼 구조체 표현식은 각 필드의 값을 어떤 순서로든 지정할 수 있어요. 필드 이름과 값은 콜론(:)으로 구분해요.

주의할 점이 하나 있어요. union 타입의 값은 이 문법으로만 만들 수 있고, 정확히 하나의 필드만 지정해야 해요.

함수형 업데이트 구문

구조체 타입의 값을 만드는 구조체 표현식은 .. 뒤에 표현식을 붙여 함수형 업데이트(functional update) 를 나타낼 수 있어요.

  • .. 뒤의 표현식(베이스)은 새로 만드는 구조체 타입과 같은 구조체 타입이어야 해요.
  • 전체 표현식은 지정된 필드에는 주어진 값을, 나머지 필드는 베이스 표현식에서 이동하거나 복사해요.
  • 다른 구조체 표현식과 마찬가지로, 명시적으로 이름을 붙이지 않은 필드까지 구조체의 모든 필드가 보여야(visible) 해요.
#![allow(unused)]
fn main() {
struct Point3d { x: i32, y: i32, z: i32 }
let mut base = Point3d {x: 1, y: 2, z: 3};
let y_ref = &mut base.y;
Point3d {y: 0, z: 10, .. base}; // OK, base.x만 접근됨
drop(y_ref);
}

위치 제한과 튜플 필드

구조체 표현식은 루프나 if 표현식의 머리(head), 또는 if let이나 match 표현식의 검사 대상(scrutinee) 에 직접 쓸 수 없어요. 하지만 괄호 안처럼 다른 표현식 안에 있으면 쓸 수 있어요.

필드 이름을 십진 정수 값으로 쓰면 튜플 구조체를 만들 때 인덱스를 지정할 수 있어요. 베이스 구조체와 함께 쓰면 지정하지 않은 나머지 인덱스를 채울 수도 있어요.

#![allow(unused)]
fn main() {
struct Color(u8, u8, u8);
let c1 = Color(0, 0, 0);  // 튜플 구조체를 만드는 일반적인 방법.
let c2 = Color{0: 255, 1: 127, 2: 0};  // 인덱스로 필드 지정.
let c3 = Color{1: 0, ..c2};  // 베이스 구조체로 나머지 필드 채우기.
}

구조체 필드 초기화 축약

이름 있는(번호 없는) 필드를 가진 데이터 구조체(struct, enum, union)를 초기화할 때, fieldname: fieldname 대신 fieldname 만 쓰는 축약 표기가 허용돼요. 중복을 줄여주는 간결한 문법이에요.

#![allow(unused)]
fn main() {
struct Point3d { x: i32, y: i32, z: i32 }
let x = 0;
let y_value = 0;
let z = 0;
Point3d { x: x, y: y_value, z: z };
Point3d { x, y: y_value, z };
}

더 알아보기 (Learn more)