2015-05-20 14 views
23

Trong Rust, chúng ta có thể sử dụng loại Box<T> để phân bổ mọi thứ trên heap. Loại này được sử dụng để đánh dấu một cách an toàn các con trỏ để lưu trữ bộ nhớ. Box<T> được cung cấp bởi thư viện chuẩn Rust.Từ khóa hộp làm gì?

Tôi tò mò về cách phân bổ Box<T> được triển khai, vì vậy tôi đã tìm thấy its source code. Dưới đây là mã cho Box<T>::new (như của Rust 1.0):

impl<T> Box<T> { 
    /// Allocates memory on the heap and then moves `x` into it. 
    /// [...] 
    #[stable(feature = "rust1", since = "1.0.0")] 
    #[inline(always)] 
    pub fn new(x: T) -> Box<T> { 
     box x 
    } 
} 

Dòng chỉ trong việc thực hiện trả về giá trị box x. Từ khóa box này không được giải thích ở bất kỳ đâu trong tài liệu chính thức; trên thực tế, nó chỉ được đề cập ngắn gọn trên trang tài liệu std::boxed.

Trả lời

15

Điều gì box x thường sử dụng để cấp phát và bộ nhớ trống?

Câu trả lời là các chức năng được đánh dấu bằng các mục lang exchange_malloc để phân bổ và exchange_free để giải phóng. Bạn có thể xem việc triển khai những người trong thư viện chuẩn mặc định tại heap.rs#L112heap.rs#L125.

Trong khi kết thúc cú pháp box x phụ thuộc vào các mục lang sau:

  • owned_box trên một struct Box để đóng gói các con trỏ được phân bổ. Cấu trúc này không cần thực hiện Drop, nó được thực hiện tự động bởi trình biên dịch.
  • exchange_malloc để cấp phát bộ nhớ.
  • exchange_free để giải phóng bộ nhớ được phân bổ trước đó.

Điều này có thể được nhìn thấy một cách hiệu quả trong lang items chương của cuốn sách gỉ sử dụng no_std ví dụ này:

#![feature(lang_items, box_syntax, start, no_std, libc)] 
#![no_std] 

extern crate libc; 

extern { 
    fn abort() -> !; 
} 

#[lang = "owned_box"] 
pub struct Box<T>(*mut T); 

#[lang = "exchange_malloc"] 
unsafe fn allocate(size: usize, _align: usize) -> *mut u8 { 
    let p = libc::malloc(size as libc::size_t) as *mut u8; 

    // malloc failed 
    if p as usize == 0 { 
     abort(); 
    } 

    p 
} 
#[lang = "exchange_free"] 
unsafe fn deallocate(ptr: *mut u8, _size: usize, _align: usize) { 
    libc::free(ptr as *mut libc::c_void) 
} 

#[start] 
fn main(argc: isize, argv: *const *const u8) -> isize { 
    let x = box 1; 

    0 
} 

#[lang = "stack_exhausted"] extern fn stack_exhausted() {} 
#[lang = "eh_personality"] extern fn eh_personality() {} 
#[lang = "panic_fmt"] fn panic_fmt() -> ! { loop {} } 

Chú ý cách Drop đã không được thực hiện cho Box struct? Vâng chúng ta hãy xem các LLVM IR tạo ra cho main:

define internal i64 @_ZN4main20hbd13b522fdb5b7d4ebaE(i64, i8**) unnamed_addr #1 { 
entry-block: 
    %argc = alloca i64 
    %argv = alloca i8** 
    %x = alloca i32* 
    store i64 %0, i64* %argc, align 8 
    store i8** %1, i8*** %argv, align 8 
    %2 = call i8* @_ZN8allocate20hf9df30890c435d76naaE(i64 4, i64 4) 
    %3 = bitcast i8* %2 to i32* 
    store i32 1, i32* %3, align 4 
    store i32* %3, i32** %x, align 8 
    call void @"_ZN14Box$LT$i32$GT$9drop.103617h8817b938807fc41eE"(i32** %x) 
    ret i64 0 
} 

Các allocate (_ZN8allocate20hf9df30890c435d76naaE) được gọi như mong đợi để xây dựng Box, trong khi đó ... Nhìn kìa! Phương thức Drop cho số Box (_ZN14Box$LT$i32$GT$9drop.103617h8817b938807fc41eE)! Hãy xem IR cho phương pháp này:

define internal void @"_ZN14Box$LT$i32$GT$9drop.103617h8817b938807fc41eE"(i32**) unnamed_addr #0 { 
entry-block: 
    %1 = load i32** %0 
    %2 = ptrtoint i32* %1 to i64 
    %3 = icmp ne i64 %2, 2097865
    br i1 %3, label %cond, label %next 

next:            ; preds = %cond, %entry- block 
    ret void 

cond:            ; preds = %entry-block 
    %4 = bitcast i32* %1 to i8* 
    call void @_ZN10deallocate20he2bff5e01707ad50VaaE(i8* %4, i64 4, i64 4) 
    br label %next 
} 

Có nó là, deallocate (ZN10deallocate20he2bff5e01707ad50VaaE) được gọi vào trình biên dịch tạo ra Drop!

Thông báo ngay cả trên standard library đặc điểm Drop không được thực thi bởi mã người dùng. Thật vậy Box là một chút của một cấu trúc kỳ diệu.

10

Trước khi box được đánh dấu là không ổn định, nó được sử dụng làm cách viết tắt để gọi Box::new. Tuy nhiên, nó luôn được dự định để có thể phân bổ các loại tùy ý, chẳng hạn như Rc hoặc sử dụng phân bổ tùy ý. Cả hai đã không được hoàn thành, vì vậy nó không được đánh dấu là ổn định cho bản phát hành 1.0. Điều này được thực hiện để ngăn chặn việc hỗ trợ một quyết định tồi cho tất cả Rust 1.x.

Để tham khảo thêm, bạn có thể đọc RFC that changed the "placement new" syntax và cũng có tính năng gated nó.

+0

Vì vậy, hiện tại (trong quá trình thực hiện ở trên) 'hộp x' là cú pháp vị trí mới? – thirtythreeforty

+1

@thirtythreeforty, vâng, đó là vị trí cú pháp mới hiện đang được mã hóa cứng để chỉ hoạt động với 'Box'. –

+0

Vì vậy, cuối cùng 'Box' sẽ được xử lý không đặc biệt hơn bất kỳ loại nào tôi có thể tạo, và nó sẽ chỉ sử dụng các tính năng ngôn ngữ khác (post-1.0). – thirtythreeforty

6

box thực hiện chính xác những gì Box::new() làm - nó tạo ra một hộp sở hữu.

Tôi tin rằng bạn không thể tìm thấy thi hành box từ khóa bởi vì hiện nay nó là hardcoded để làm việc với hộp nước, và Box loại là một mục lang:

#[lang = "owned_box"] 
#[stable(feature = "rust1", since = "1.0.0")] 
#[fundamental] 
pub struct Box<T>(Unique<T>); 

Bởi vì nó là một mục lang, các trình biên dịch có logic đặc biệt để xử lý sự khởi tạo của nó mà nó có thể liên kết với từ khóa box.

Tôi tin rằng trình biên dịch ủy quyền phân bổ hộp cho các hàm trong mô-đun alloc::heap.

Đối với những gì box từ khóa và có nghĩa vụ phải làm nói chung, câu trả lời của Shepmaster mô tả hoàn hảo.

Các vấn đề liên quan