[Rust] Struct

고승우·2024년 7월 23일
0

Rust

목록 보기
13/16
post-thumbnail

Struct

struct assembles several values of assorted types together into a single value so you can deal with them as a unit. Rust has three kind of struct types, named-field, tuple-like, unit-like, which differ in how you refer to their components: - named-field: gives a name to each component

  • tuple-like: Identifies them by the order in which they appear
  • unit-like:

Named-Field Structs

Named-Field Structs gives a name to each component and struct type looks like this

/// A rectangle of eight-bit grayscale pixels.
struct GrayscaleMap {
    pixels: Vec<u8>,
    size: (usize, usize),
}

Tuple-Like Structs

Tuple-like Structs resembles a tuple. The values held by a tuple-like struct are called elements, just as the values of a tuple are.

struct Bounds(usize, usize);

let image_bounds = Bounds(1024, 768);

The expression Bounds(1024, 768) work like function defining the type also implicitly defines a function:

fn Bounds(elem0: usize, elem1: usize) -> Bounds { ... }

Tuple-like structs are good for newtypes like this:

struct Ascii(Vec<u8>);

Struct Layout

In memory, both named-field and tuple-like structs are the same thing: a collection of values, of possibly mixed types, laid out in a particular way in memory. For example, earlier in the chapter we defined this struct:

struct GrayscaleMap {
    pixels: Vec<u8>,
    size: (usize, usize),
}


Rust embeds pixels and size directly in the GrayscaleMap value. Only the heap-allocated buffer owned by the pixels vector remains in its own block.

Defining Methods with impl

An impl block is simply a collection of fn definitions, each of which becomes a method.

#[derive(Debug)]
struct Pixel {
    red: u8,
    green: u8,
    blue: u8,
}

impl Pixel {
    fn rev(&mut self) {
        self.red = u8::MAX - self.red;
        self.green = u8::MAX - self.green;
        self.blue = u8::MAX - self.blue;
    }

    fn show(&self) {
        println!("{:?}", &self);
    }
}

#[test]
fn test_impl() {
    let mut pix = Pixel {
        red: 130,
        green: 40,
        blue: 210,
    };
    pix.show(); // Pixel { red: 130, green: 40, blue: 210 }

    pix.rev();
    pix.show(); // Pixel { red: 125, green: 215, blue: 45 }
}

Passing Self as a Box, Rc, or Arc

A method’s self argument can also be a Box<Self>, Rc<Self>, or Arc<Self>. Such a method can only be called on a value of the given pointer type. Those types of arguments can manage ownership of the pointer. Suppose we have a tree of nodes like this:

#[derive(Debug)]
struct Node<'a> {
    tag: &'a str,
    children: Vec<Rc<Node<'a>>>,
}
impl <'a>Node<'a> {
    fn new(tag: &str) -> Node {
        Node {
            tag: tag,
            children: vec![],
        }
    }
    const ROOT: Node<'a> = Node{ tag: "Root", children: vec![] };
}

Let's add a method that appends it to some other Node’s children:

fn append_to(self, parent: &mut Node<'a>) {
	parent.children.push(Rc::new(self));
}

This method calls Rc::lnew to allocate a fresh heap location and move itself into it.

fn append_to(self: Rc<Self>, parent: &mut Node<'a>) {
        parent.children.push(self);
    }

This passes ownership of shared_node to the method: no reference counts are adjusted, and there’s certainly no new allocation

Finally, we can use this struct like this:

let mut parent = Node::new("parent");
let owned = Node::new("owned directly");
Rc::new(owned).append_to(&mut parent);

The caller is able to minimize allocation and reference-counting activity given its own needs.

  • If it can pass ownership of the Rc, it simply hands over the pointer
  • If it needs to retain ownership of an Rc, it just bumps the reference count
  • Only if it owns the Node itself must it call Rc::new to allocate heap space and move the Node into it. Since parent will insist on referring to its children via Rc<Node> pointers, this was going to be necessary eventually.

Associated Consts

As the name implies, associated consts are constant values. They’re often used to specify commonly used values of a type.

impl <'a>Node<'a> {
    const ROOT: Node<'a> = Node{ tag: "Root", children: vec![] };
}

you can use them without referring to another instance of Node

let mut root = Node::ROOT;

Generic Structs

Rust structs can be generic, meaning that their definition is a template into which you can plug whatever types you like.

pub struct MyQueue<T> {
    queue: Vec<T>,
}

impl<T> MyQueue<T> {
    pub fn new() -> MyQueue<T> {
        MyQueue { queue: vec![] }
    }
    pub fn push(&mut self, t: T) {
        self.queue.push(t);
    }
}

You can constrain a type to ensure it implements a specific trait. This allows you to use the trait's functions, such as to_string.

impl<T: ToString> MyQueue<T> {
    pub fn new() -> MyQueue<T> {
        MyQueue { queue: vec![] }
    }
    pub fn push(&mut self, t: T) {
        self.queue.push(t);
    }
    pub fn to_string_vec(&mut self) {
        self.queue.iter_mut().map(|element| {element.to_string();});
    }
}

Structs with LifeTime Parameters

If a struct type contains references, you must name those references’ lifetimes.

struct Extrema<'elt> {
    greatest: &'elt i32,
    least: &'elt i32,
}

As you see, lifetime parameters position same as Generic. If you use both of them, you should write the code like:

pub struct MyQueue<'a, T> {
    queue: Vec<&'a T>,
}

impl<'a, T: ToString> MyQueue<'a, T> {
    pub fn new() -> MyQueue<'a, T> {
        MyQueue { queue: vec![] }
    }
    pub fn push(&mut self, t: &'a T) {
        self.queue.push(t);
    }
    pub fn to_string_vec(&mut self) {
        self.queue.iter_mut().map(|element| {
            element.to_string();
        });
    }
}

Interior Mutability

How can we define struct as "partially mutable"? We can make it with Cell or RefCell. You can get a information about Cell and RefCell at this page.

struct MySystem {
        systems: Vec<&'static str>,
        systems_with_cell: Cell<Vec<&'static str>>,
    }
    
let my_system = MySystem{ systems: vec![], systems_with_cell: Cell::new(vec![])};
my_system.systems = vec!["Notification"];
my_system.systems_with_cell.set(vec!["Notification"]);
profile
٩( ᐛ )و 

0개의 댓글