스택에 할당 된다.힙에 할당 된다.fn main () {
println!("{}", '\n');
// 1. String 타입의 문자열 객체 선언
let string_타입 = String::from("힙");
// 2. 선언된 String 타입의 주소를 String_타입_주소에 복사합니다.
let string_타입_주소 = &string_타입;
// 3. 변수의 주소를 출력합니다.
// 3-1. 힙에 있는 실제 객체의 주소
println!("힙에 있는 실제 객체의 주소 {:p} \n", string_타입.as_ptr());
// 3-2. 스택에 있는 변수의 실제 주소
println!("스택에 있는 변수의 실제 주소 {:p} \n", string_타입_주소);
// 4. String 타입의 소유권을 이동시킵니다.
let string_타입_새로운_소유자 = string_타입;
// 5. 이동시킨 String 타입의 주소를 복사합니다.
let string_타입_새로운_소유자_주소 = &string_타입_새로운_소유자;
// 6. 변수의 주소를 출력합니다.
// 6-1. 힙에 있는 실제 객체의 주소
println!("힙에 있는 실제 객체의 주소 {:p} \n", string_타입_새로운_소유자.as_ptr());
// 6-2. 새로 생성된 소유자의 스택 주소
println!("스택에 있는 변수의 실제 주소 {:p} \n", string_타입_새로운_소유자_주소);
println!("{}", '\n');
}

pub struct String {
vec: Vec<u8>,
}
let 스트링_타입 = String::new();
let 이것도_스트링_타입 = "스트링타입".to_string();
let 난_str_타입 = "data";
let 이렇게하면_스트링_타입 = 난_str_타입.to_string();
let 바로_값_넣고_할당 = String::from("스트링 타입이에요");
let mut string = String::new();
// char 타입은 따옴표로 감싸야 한다.
string.push('일');
// str타입은 쌍따옴표로 감싸야한다.
string.push_str("하나");
dbg!(string);
push따옴표로 감싸야 한다.push_str쌍따옴표로 감싸야한다. let str1 = String::from("Hello, ");
let str2 = String::from("world!");
let str3 = str1 + &str2;
dbg!(str1); // use of moved value: `s1` value used hear after move
dbg!(str2);
dbg!(str3);
```rust
let str1 = String::from("Hello, ");
let str2 = String::from("world!");
let str3 = str1 + str2; // expected &str, found String
dbg!(str1);
dbg!(str2);
dbg!(str3);
첫 str1은 self로 구조체 자기 자신이 된다.두 번째 코드 스니펫을 보게되면 &str 값이 들어와야 된다고 하는데 String이 들어와 있어 컴라일러가 에러를 뱉어낸다.
🤔 하지만, &str2는 &str 타입이 아니라 &String 타입이다. 어떻게 &String 타입을 넣을 수 있을까?
☑ Rust는 &String을 &str로 강제한다!
Rust에서 deref coercion(역참조 강제)는 메서드나 함수 호출 시 자료형을 자동으로 역참조하는 언어 기능이다! << 조금더 공부가 필요하겠다 ㅠㅠ
결국 String 타입의 덧셈은 더하기 연산자의 첫 번째 매개 변수의 소유권을 가지고 두 번째 매개 변수의 값을 참조하여 더한 뒤 소유권을 반환하는 과정이다!
format! 매크로
😎 format 매크로는 소유권을 가지지 않고 더해주는 멋진 녀석이다!
let string1 = String::from("김");
let string2 = String::from("동");
let string3 = String::from("현");
let string_add = format!("{}{}{}", string1, string2 ,string3);
dbg!(string1);
dbg!(string2);
dbg!(string3);
dbg!(string_add);
let string1 = String::from("hello");
let idx = string1[0];
//the type `String` cannot be indexed by `{integer}` the trait `Index<{integer}>` is not implemented for `String` the following other types implement trait `Index<Idx>`:
러스트의 String 타입은 인덱싱을 지원하지 않는다!!
위에 이야기 한 것 처럼 String은 문자열이 아니라 Vec의 Wrapper이다.
또한, Rust의 String은 UTF-8을 기준으로 문자열을 관리한다.
🤔 UTF-8은 문자열마다 가질 수 있는 바이트의 길이가 최소 1바이트에서 최대 4바이트로 고정된 바이트 개수를 가지지 않는다!!
-> 그렇기에 인덱싱으로 전달되는 숫자가 언제나 새로운 단어의 시작이라는 점을 항상 만족할 수가 없다!
let string1 = String::from("abcde");
dbg!(string1.len()); // 5
let string2 = String::from("ㄱㄴㄷㄹㅁ");
dbg!(string2.len()); // 15
// string2의 7번째를 찍게 되면 완성되지 않은 ㄷ의 중간쯤이지 않을까?
fn main () {
let string2 = String::from("ㄱㄴ");
dbg!(&string2); // 15
for c in string2.chars() {
println!("{}", c);
}
for c in string2.bytes() {
println!("{}", c);
}
// 227
// 132
// 177
// 227
// 132
// 180
dbg!(&string2[0..3]); // ㄱ
dbg!(&string2[0..4]); // it is inside 'ㄴ' (bytes 3..6) of `ㄱㄴ`'
}
즉, 문자열 슬라이싱은 어렵다..
영어, 한국어, 아랍어 등등 가지고 있는 문자열의 바이트 개수들이 다르기 때문이다!!
😂 러스트의 String은... 주의 해서 쓰도록 하자
러스트의 String 타입은 Vec<u8>의 Wrapper 타입이다.
러스트의 String 타입은 소유권을 가지며 어느 한 시점에 반드시 하나의 소유자가 존재한다.
String의 변수는 Stack 영영에 존재하고, 실제 값은 Heap 영역에 존재한다.
Rust의 문자열은 UTF-8을 기준으로 하기에 슬라이싱 하는 데 어려움이 있다.
String 타입을 소유권을 가지는 함수가 사용하고 난 뒤에는 사라지게 된다!
String의 더하기 연산자(+)는 첫 번째 피 연산자의 소유권을 가지고 두 번재 피연산자의 값을 참조하여 더한 뒤 뱉어내는 메서드이다.