link
- 1.1📌 Comparison table
- 1.2🧠 Why Vec feels like a smart pointer
- 1.3❌ Why Vec
is NOT a smart pointer - 1️⃣
Vec<T>is a collection, not a pointer abstraction - 2️⃣
Vec<T>does NOT implementDeref<Target = T> - 3️⃣ Moving a
Vec<T>DOES NOT pin its elements - 4️⃣
Vec<T>allows reallocation (key difference)
- 1️⃣
Dynamic Array
⚠️ Important contrast:
Box<[T]>- IS closer to a smart pointerCost of Collection Operations성능비교
VecvsVecDequevsLinkedListvsHashMapvsBTreeMap만들어 보면서 익히기
Vector Basics in Rust(10 Example)
| Example | Topic |
|---|---|
| 1 | Create & push |
| 2 | vec![] macro |
| 3 | Index access |
| 4 | Safe access (get) |
| 5 | Iteration |
| 6 | Mutable iteration |
| 7 | Pop |
| 8 | Capacity |
| 9 | Enums in Vec |
| 10 | Ownership |
Vector공식문서|🔝|
Cost of Collection Operations|🔝|
| get(i) | insert(i) | remove(i) | append(Vec(m)) | split_off(i) | range | append | |
|---|---|---|---|---|---|---|---|
[Vec] | O(1) | O(n-i)* | O(n-i) | O(m)* | O(n-i) | N/A | N/A |
[VecDeque] | O(1) | O(min(i, n-i))* | O(min(i, n-i)) | O(m)* | O(min(i, n-i)) | N/A | N/A |
[LinkedList] | O(min(i, n-i)) | O(min(i, n-i)) | O(min(i, n-i)) | O(1) | O(min(i, n-i)) | N/A | N/A |
[HashMap] | O(1)~ | O(1)~* | O(1)~ | N/A | N/A | N/A | N/A |
[BTreeMap] | O(log(n)) | O(log(n)) | O(log(n)) | N/A | N/A | O(log(n)) | O(n+m) |
Performance
| Peek (index) | Mutate (insert or delete) at … | Excess space, average | |||
|---|---|---|---|---|---|
| Beginning | End | Middle | |||
| Linked list | Θ(n) | Θ(1) | Θ(1), known end element; Θ(n), unknown end element | Θ(n) | Θ(n) |
| Array | Θ(1) | N/a | N/a | N/a | 0 |
| Dynamic array | Θ(1) | Θ(n) | Θ(1) amortized | Θ(n) | Θ(n)[15] |
| Balanced tree | Θ(log n) | Θ(log n) | Θ(log n) | Θ(log n) | Θ(n) |
| Random-access list | Θ(log n)[16] | Θ(1) | N/a[16] | N/a[16] | Θ(n) |
| Hashed array tree | Θ(1) | Θ(n) | Θ(1) amortized | Θ(n) | Θ(√n) |
(250125)In search of the perfect dynamic array growth factor | Kay Lack|🔝|
Chapters
00:00 - Intro
03:29 - Golden Ratio
05:11 - 1.5 Factor
07:08 - Lankinen Algorithm
09:53 - Larger simulations
14:18 - Origins
17:36 - Exercise언어별 Growth factor(dynamic array)|🔝|
| Implementation | Growth factor (a) |
|---|---|
| Java ArrayList[1] | 1.5 (3/2) |
| Python PyListObject[7] | ~1.125 (n + (n >> 3)) |
| Microsoft Visual C++ 2013[8] | 1.5 (3/2) |
| G++ 5.2.0[5] | 2 |
| Clang 3.6[5] | 2 |
| Facebook folly/FBVector[9] | 1.5 (3/2) |
| Unreal Engine TArray[10] | ~1.375 (n + ((3 * n) >> 3)) |
| Rust Vec[11] | 2 |
| Go slices[12] | between 1.25 and 2 |
| Nim sequences[13] | 2 |
| SBCL (Common Lisp) vectors[14] | 2 |
| C# (.NET 8) List | 2 |
1. Is Vec<T> a Smart Pointer?|🔝|
- ✅ Official answer (Rust terminology)
Vec<T> is NOT classified as a smart pointer.In Rust, smart pointers are types whose primary purpose is ownership management via pointer semantics, usually by implementing:
Deref/DerefMut- custom
Drop - pointer-like behavior (
Box,Rc,Arc, etc.)
Examples:
Box<T>Rc<T>Arc<T>RefCell<T>Mutex<T>
Vec<T>does not fall into this category
1.1📌 Comparison table|🔝|
| Type | Smart Pointer? | Deref Target | Reallocates | Points to |
|---|---|---|---|---|
Box<T> | ✅ | T | ❌ | one value |
Rc<T> | ✅ | T | ❌ | one value |
Arc<T> | ✅ | T | ❌ | one value |
Vec<T> | ❌ | [T] | ✅ | many values |
Box<[T]> | ❌ (collection) | [T] | ❌ | many values |
🔑 Correct mental model|🔝|
Vec<T> = owning, growable buffer
Smart pointer = owning pointer to a single valueOne-sentence takeaway|🔝|
Vec<T> is not a smart pointer in Rust because it is a growable collection that dereferences to a slice and may reallocate its elements, whereas smart pointers represent stable ownership of a single value.1.2🧠 Why Vecfeels like a smart pointer|🔝|
- Because internally,
Vec<T>does own heap memory and manages it automatically. - Internal layout (simplified)
Vec<T> (stack)
┌──────────────┐
│ ptr │──┐
│ len │ │
│ cap │ │
└──────────────┘ │
▼
Heap
┌────────────────────────┐
│ T | T | T | ... │
└────────────────────────┘- This looks pointer-like, but that alone does not make it a smart pointer.
1.3❌ Why Vec<T> is NOT a smart pointer|🔝|
1️⃣ Vec<T> is a collection, not a pointer abstraction|🔝|
- Smart pointers conceptually point to one value:
Box<T> → one T
Arc<T> → one TVec<T>manages many values.
2️⃣ Vec<T> does NOT implement Deref<Target = T>|🔝|
let v: Vec<i32>;
*v // ❌ does not compile- Instead:
v[i] // indexing
v.as_slice()- This is intentional.
- 이것은 의도적입니다.
3️⃣ Moving a Vec<T> DOES NOT pin its elements|🔝|
let mut v = vec![1, 2, 3];
let p = &v[0];
let v2 = v; // move- This is legal because:
• v2 points to same allocation
• but elements MAY move on reallocationSmart pointers promise stable ownership of a single pointee.
- 스마트 포인터는 단일 포인티의 안정적인 소유권을 보장합니다.
Vecdoes not.- ‘Vec’은 그렇지 않습니다.
4️⃣ Vec<T> allows reallocation (key difference)|🔝|
v.push(4); // may reallocate- This can move all elements.
- Smart pointers never reallocate their pointee.
- 이것은 모든 요소를 이동시킬 수 있습니다.
- 똑똑한 포인터는 결코 그들의 포인트를 재배치하지 않습니다.
Vector Basic(10 examples)|🔝|
1. Create an empty vector and push elements|🔝|
fn main() {
let mut v: Vec<i32> = Vec::new();
v.push(10);
v.push(20);
v.push(30);
println!("{:?}", v);
}- Concepts
Vec::new()push- mutable vector
2. Create a vector with initial values|🔝|
fn main() {
let v = vec![1, 2, 3, 4, 5];
println!("{:?}", v);
}- Concepts
vec![]macro- type inference
3. Access elements by index|🔝|
fn main() {
let v = vec![10, 20, 30];
let first = v[0];
let second = v[1];
println!("{} {}", first, second);
}- ⚠️ Panics if index is out of bounds.
4. Safe access using get|🔝|
fn main() {
let v = vec![10, 20, 30];
match v.get(1) {
Some(value) => println!("Value: {}", value),
None => println!("Out of bounds"),
}
}- Concepts
Option<T>- bounds safety
5. Iterate over a vector|🔝|
fn main() {
let v = vec![1, 2, 3];
for x in &v {
println!("{}", x);
}
}- Concepts
- borrowing (
&v) - iteration
- borrowing (
6. Modify elements using mutable iteration|🔝|
fn main() {
let mut v = vec![1, 2, 3];
for x in &mut v {
*x *= 10;
}
println!("{:?}", v);
}- Concepts
- mutable references
- dereferencing
*x
7. Pop elements from a vector|🔝|
fn main() {
let mut v = vec![1, 2, 3];
let last = v.pop();
println!("{:?}", last);
println!("{:?}", v);
}- Concepts
pop- returns
Option<T>
8. Vector length and capacity|🔝|
fn main() {
let mut v = Vec::with_capacity(10);
v.push(1);
v.push(2);
println!("len = {}", v.len());
println!("capacity = {}", v.capacity());
}- Concepts
lencapacity- preallocation
9. Store different data using enums|🔝|
enum Value {
Int(i32),
Float(f64),
Text(String),
}
fn main() {
let v = vec![
Value::Int(10),
Value::Float(3.14),
Value::Text(String::from("hello")),
];
for item in v {
match item {
Value::Int(i) => println!("Int: {}", i),
Value::Float(f) => println!("Float: {}", f),
Value::Text(s) => println!("Text: {}", s),
}
}
}- Concepts
- homogeneous
Vec<T> - enums for heterogeneity
- homogeneous
10. Ownership and moving vectors|🔝|
fn consume(v: Vec<i32>) {
println!("{:?}", v);
}
fn main() {
let v = vec![1, 2, 3];
consume(v);
// println!("{:?}", v); ❌ compile error
}- Concepts
- ownership transfer
- move semantics
⚠️ Important contrast: Box<[T]>|🔝|
- ⚠️ 중요한 대비
Box<[T]>- IS closer to a smart pointer:
• owns heap memory
• fixed size
• no reallocation
• single owned slice- But still:
Collection ≠ smart pointer🧩 Why Rust documentation is strict here|🔝|
🧩 여기서 러스트 문서가 엄격한 이유
Rust defines smart pointers as:
Types that own data on the heap and provide additional functionality through dereferencing
Vec<T>owns heap data, but does not deref to a singleT.Instead:
러스트는 스마트 포인터를 다음과 같이 정의합니다:
힙에 데이터를 소유하고 참조 해제를 통해 추가 기능을 제공하는 유형
‘Vec’는 힙 데이터를 소유하고 있지만, 단일 ‘T’를 의미하지는 않습니다.
대신:
impl<T> Deref for Vec<T> {
type Target = [T];
}So it derefs to a slice, not a value.
That alone disqualifies it from being considered a smart pointer in Rust terms.
그래서 그것은 값이 아니라 한 조각으로 떨어집니다.
그것만으로도 Rust 용어로는 스마트 포인터로 간주되지 않습니다.
Vector 내 맘대로 만들어 보기 하지만 cargo miri를 해보니 UB가 발생함.|🔝|
- 문제의 코드
use std::mem::MaybeUninit;
use std::ptr;
#[derive(Debug)]
pub struct Stack<T, const N: usize> {
data: [MaybeUninit<T>; N],
len: usize,
}
impl<T, const N: usize> Stack<T, N> {
/// Create an empty stack
pub fn new() -> Self {
// SAFETY: An uninitialized array of MaybeUninit is valid
let data = unsafe { MaybeUninit::<[MaybeUninit<T>; N]>::uninit().assume_init() };
Stack { data, len: 0 }
}
/// Push to top of stack
pub fn push(&mut self, value: T) -> Result<(), T> {
if self.len == N {
return Err(value); // stack overflow
}
self.data[self.len].write(value);
self.len += 1;
Ok(())
}
/// Pop from top of stack
pub fn pop(&mut self) -> Option<T> {
if self.len == 0 {
return None;
}
self.len -= 1;
// SAFETY: element was initialized
Some(unsafe { self.data[self.len].assume_init_read() })
}
/// Push to bottom (front) of stack
pub fn front_push(&mut self, value: T) -> Result<(), T> {
if self.len == N {
return Err(value);
}
unsafe {
// Shift elements right
ptr::copy(self.data.as_ptr(), self.data.as_mut_ptr().add(1), self.len);
}
self.data[0].write(value);
self.len += 1;
Ok(())
}
/// Peek top element
pub fn peek(&self) -> Option<&T> {
if self.len == 0 {
None
} else {
// SAFETY: element is initialized
Some(unsafe { &*self.data[self.len - 1].as_ptr() })
}
}
pub fn len(&self) -> usize {
self.len
}
pub fn is_empty(&self) -> bool {
self.len == 0
}
pub fn capacity(&self) -> usize {
N
}
}
impl<T, const N: usize> Drop for Stack<T, N> {
fn drop(&mut self) {
for i in 0..self.len {
unsafe {
self.data[i].assume_init_drop();
}
}
}
}
pub struct IntoIter<T, const N: usize> {
stack: Stack<T, N>,
}
impl<T, const N: usize> Iterator for IntoIter<T, N> {
type Item = T;
fn next(&mut self) -> Option<Self::Item> {
self.stack.pop()
}
}
impl<T, const N: usize> IntoIterator for Stack<T, N> {
type Item = T;
type IntoIter = IntoIter<T, N>;
fn into_iter(self) -> Self::IntoIter {
IntoIter { stack: self }
}
}
pub struct Iter<'a, T, const N: usize> {
stack: &'a Stack<T, N>,
index: usize,
}
impl<'a, T, const N: usize> Iterator for Iter<'a, T, N> {
type Item = &'a T;
fn next(&mut self) -> Option<Self::Item> {
if self.index == 0 {
return None;
}
self.index -= 1;
// SAFETY: element is initialized
Some(unsafe { &*self.stack.data[self.index].as_ptr() })
}
}
pub struct IterMut<'a, T, const N: usize> {
stack: &'a mut Stack<T, N>,
index: usize,
}
impl<'a, T, const N: usize> Iterator for IterMut<'a, T, N> {
type Item = &'a mut T;
fn next(&mut self) -> Option<Self::Item> {
if self.index == 0 {
return None;
}
self.index -= 1;
// SAFETY: element is initialized and we have exclusive access
Some(unsafe { &mut *self.stack.data[self.index].as_mut_ptr() })
}
}
impl<T, const N: usize> Stack<T, N> {
pub fn iter(&self) -> Iter<'_, T, N> {
Iter { stack: self, index: self.len }
}
pub fn iter_mut(&mut self) -> IterMut<'_, T, N> {
let index = self.len;
IterMut { stack: self, index }
}
}
fn main() {
let mut stack: Stack<i32, 8> = Stack::new();
stack.push(10).unwrap();
stack.push(20).unwrap();
stack.front_push(5).unwrap();
assert_eq!(stack.pop(), Some(20));
assert_eq!(stack.pop(), Some(10));
assert_eq!(stack.pop(), Some(5));
assert_eq!(stack.pop(), None);
let mut stack02: Stack<i32, 8> = Stack::new();
stack02.push(200).unwrap();
stack02.push(100).unwrap();
stack02.push(100).unwrap();
stack02.push(100).unwrap();
stack02.push(100).unwrap();
stack02.front_push(10).unwrap();
println!("stack 02 : {:?}", stack02);
for i in stack02.iter() {
println!("stack 02 : {:?}", i);
}
let data_store = stack02.pop();
println!("data_store: {:?}", data_store);
}- cargo miri로 문제를 잡아냄.(nightly)(UB발생)
$ cargo miri run
error: Undefined Behavior: attempting a read access using <288> at alloc117[0x0], but that tag does not exist in the borrow stack for this location
--> /home/gy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:638:9
|
638 | crate::intrinsics::copy(src, dst, count)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this error occurs as part of an access at alloc117[0x0..0x8]
|
= help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
= help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
help: <288> was created by a SharedReadOnly retag at offsets [0x0..0x20]
--> src/main.rs:50:23
|
50 | ptr::copy(self.data.as_ptr(), self.data.as_mut_ptr().add(1), self.len);
| ^^^^^^^^^^^^^^^^^^
help: <288> was later invalidated at offsets [0x0..0x20] by a Unique retag
--> src/main.rs:50:43
|
50 | ptr::copy(self.data.as_ptr(), self.data.as_mut_ptr().add(1), self.len);
| ^^^^^^^^^
= note: stack backtrace:
0: std::ptr::copy::<std::mem::MaybeUninit<i32>>
at /home/gy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ptr/mod.rs:638:9: 638:49
1: Stack::<i32, 8>::front_push
at src/main.rs:50:13: 50:83
2: main
at src/main.rs:167:5: 167:24
3: <fn() as std::ops::FnOnce<()>>::call_once - shim(fn())
at /home/gy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5: 250:71
4: std::sys::backtrace::__rust_begin_short_backtrace::<fn(), ()>
at /home/gy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/sys/backtrace.rs:166:18: 166:21
5: std::rt::lang_start::<()>::{closure#0}
at /home/gy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:206:18: 206:75
6: std::ops::function::impls::<impl std::ops::FnOnce<()> for &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>::call_once
at /home/gy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/core/src/ops/function.rs:287:13: 287:31
7: std::panicking::catch_unwind::do_call::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>
at /home/gy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:581:40: 581:43
8: std::panicking::catch_unwind::<i32, &dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe>
at /home/gy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:544:19: 544:88
9: std::panic::catch_unwind::<&dyn std::ops::Fn() -> i32 + std::marker::Sync + std::panic::RefUnwindSafe, i32>
at /home/gy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:359:14: 359:40
10: std::rt::lang_start_internal::{closure#0}
at /home/gy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:175:24: 175:49
11: std::panicking::catch_unwind::do_call::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>
at /home/gy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:581:40: 581:43
12: std::panicking::catch_unwind::<isize, {closure@std::rt::lang_start_internal::{closure#0}}>
at /home/gy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panicking.rs:544:19: 544:88
13: std::panic::catch_unwind::<{closure@std::rt::lang_start_internal::{closure#0}}, isize>
at /home/gy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/panic.rs:359:14: 359:40
14: std::rt::lang_start_internal
at /home/gy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:171:5: 193:7
15: std::rt::lang_start::<()>
at /home/gy/.rustup/toolchains/nightly-x86_64-unknown-linux-gnu/lib/rustlib/src/rust/library/std/src/rt.rs:205:5: 210:6
error: aborting due to 1 previous error
unsafe {
// Shift elements right
ptr::copy(self.data.as_ptr(), self.data.as_mut_ptr().add(1), self.len);
}
unsafe {
// Get raw pointer once - use it for both source and destination
let data_ptr = self.data.as_mut_ptr();
// Shift elements right by copying from right to left
for i in (0..self.len).rev() {
ptr::copy(data_ptr.add(i), data_ptr.add(i + 1), 1);
}
}