{"id":404262,"date":"2024-06-29T17:36:17","date_gmt":"2024-06-29T17:36:17","guid":{"rendered":"http:\/\/savepearlharbor.com\/?p=404262"},"modified":"-0001-11-30T00:00:00","modified_gmt":"-0001-11-29T21:00:00","slug":"","status":"publish","type":"post","link":"https:\/\/savepearlharbor.com\/?p=404262","title":{"rendered":"<span>How does Rust treat Strings and Vectors internally<\/span>"},"content":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h2>1. Strings<\/h2>\n<p>In Rust strings can be represented in two ways:<\/p>\n<p>a)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 String type <\/p>\n<p>b)\u00a0\u00a0\u00a0\u00a0\u00a0 String slice<\/p>\n<p><strong>String type:<\/strong><\/p>\n<p>String type is defined as a struct of the following structure:<\/p>\n<p>Depending on arch (in my case x86 64bit it is 24byte)<\/p>\n<p><em>{<\/em><\/p>\n<p><em>\u00a0\u00a0 pointer to the address where string characters are stored (8b)<\/em><\/p>\n<p><em>\u00a0\u00a0 capacity (8b)<\/em><\/p>\n<p><em>\u00a0\u00a0 length (8b)<\/em><\/p>\n<p><em>}<\/em><\/p>\n<p><em>\u00a0<\/em>Example:<\/p>\n<pre><code class=\"rust\">let my_string = String::from(\"hello\");<\/code><\/pre>\n<p><strong>String slice (String slice has type &amp;str):<\/strong><\/p>\n<p>There are two types of slices:<\/p>\n<p><strong><em>a)\u00a0\u00a0\u00a0\u00a0\u00a0 built from Strings<\/em><\/strong><\/p>\n<pre><code class=\"rust\">let my_string_slice = &amp;my_string[3..];<\/code><\/pre>\n<p>The goal of such slice is to point on some part of the string<\/p>\n<p>The structure of such slice similar to String:<\/p>\n<p><em>{<\/em><\/p>\n<p><em>\u00a0\u00a0 pointer to the address where string characters are stored (8b)<\/em><\/p>\n<p><em>\u00a0\u00a0 length (8b)<\/em><\/p>\n<p><em>\u00a0\u00a0 starting index (8b)<\/em><\/p>\n<p><em>}<\/em><\/p>\n<p><strong><em>b)\u00a0\u00a0\u00a0\u00a0\u00a0 built from string literals<\/em><\/strong><\/p>\n<pre><code class=\"rust\">let my_str = \"Hello\";<\/code><\/pre>\n<p>It has reduced size:<\/p>\n<p><em>{<\/em><\/p>\n<p><em>\u00a0\u00a0 pointer to the address where string characters are stored (8b)<\/em><\/p>\n<p><em>\u00a0\u00a0 length (8b)<\/em><\/p>\n<p><em>}<\/em><\/p>\n<p><strong>Memory layout:<\/strong><\/p>\n<p>So let\u2019s have a look how does these two types lays on memory:<\/p>\n<p>Let\u2019s consider couple definitions:<\/p>\n<pre><code class=\"rust\">let my_str :&amp;str = \"hello\"; let my_string1: String = my_str.to_string(); let my_string2: String = String::from(\"hello\"); let my_string_slice: &amp;str = &amp;my_string2[1..4];<\/code><\/pre>\n<p>We have defined 4 local variables:<\/p>\n<p>my_str \u2013 is a slice defined from string literals \u201chello\u201d. Such string literals are part of the code(data segment more precisely)<\/p>\n<p>my_string1 \u2013 result of creation String from the slice using to_string method;<\/p>\n<p>my_string2 \u2013 string creation using from method.<\/p>\n<p>my_string_slice \u2013 slice from string<\/p>\n<p>\u00a0The following picture shows memory layout for each variable:<\/p>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/805\/e16\/8d0\/805e168d05e0e4b20d6bdaa73eb6aab8.png\" width=\"1082\" height=\"836\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/805\/e16\/8d0\/805e168d05e0e4b20d6bdaa73eb6aab8.png\"\/><figcaption><\/figcaption><\/figure>\n<p>As you can see first variable(&amp;str) is created in stack and pointing to the static location of our code where string literals of \u201chello\u201d are placed<\/p>\n<p>Then when we call to_string() the new variable my_string1 is created and memory on heap is allocated to place \u201chello\u201d chars there.<\/p>\n<p>Same happens when we create my_string2 variable.<\/p>\n<p>Creation of my_string_slice from my_string2 leads to pointing to the same area in heap where my_string2 characters are placed but with shift in address considering starting index<\/p>\n<h2>2.\u00a0\u00a0\u00a0\u00a0 Vectors<\/h2>\n<p>Vectors in rust are dynamically extended data structures;<\/p>\n<p>They have similar structure as String:<\/p>\n<p><em>{<\/em><\/p>\n<p><em>\u00a0\u00a0 pointer to the address in the heap where data are stored (8b)<\/em><\/p>\n<p><em>\u00a0\u00a0 capacity (8b)<\/em><\/p>\n<p><em>\u00a0\u00a0 length (8b)<\/em><\/p>\n<p><em>}<\/em><\/p>\n<p>Let\u2019s have a look what happens when we create new vector and push some data to it (continue of the previous example with strings).<\/p>\n<pre><code class=\"rust\">let mut str_vec = Vec::new(); str_vec.push(my_string1); str_vec.push(my_string2);<\/code><\/pre>\n<figure class=\"full-width\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/habrastorage.org\/r\/w1560\/getpro\/habr\/upload_files\/166\/0f4\/1db\/1660f41db1d96b35bbb7119b882c205f.png\" width=\"1082\" height=\"1096\" data-src=\"https:\/\/habrastorage.org\/getpro\/habr\/upload_files\/166\/0f4\/1db\/1660f41db1d96b35bbb7119b882c205f.png\"\/><figcaption><\/figcaption><\/figure>\n<p>So as we see when we created str_vec variable of type Vec&lt;String> the new memory were allocated in heap. To be more precise during variable creation using new() method pointer of the vector doesn\u2019t point to real heap space. This happen only with first push:<\/p>\n<p>Memory dump:<\/p>\n<pre><code class=\"rust\">let mut str_vec = Vec::new();<\/code><\/pre>\n<p>pointer = 0x3a772ff2f8 (&amp;<u>str_vec<\/u>)<\/p>\n<p>08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00<\/p>\n<pre><code class=\"rust\">str_vec.push(my_string1);<\/code><\/pre>\n<p>50 5e ca db db 01 00 00 04 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00<\/p>\n<pre><code class=\"rust\">str_vec.push(my_string2);<\/code><\/pre>\n<p>50 5e ca db db 01 00 00 04 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00<\/p>\n<p>So as you can see real address pointing to the heap(0x000001dbdbca5e50) appears only after first push. Initial capacity 4 is reserved at this time as well. When more the 4 elements will be pushed the new area will be allocated with extended size.<\/p>\n<p>\u00a0When <em>my_string1<\/em> and <em>my_string2<\/em> were pushed to the vector two things happened:<\/p>\n<p>1)\u00a0\u00a0\u00a0\u00a0\u00a0 Memory allocation in heap for storing two string variables. Pay attention that pointers of the strings still point to same addresses were chars were stored.<\/p>\n<p>2)\u00a0\u00a0\u00a0\u00a0\u00a0 Move of ownership: <em>my_string<\/em>1 and <em>my_string<\/em>2 are no more accessible as local variables. (see more details from official rust tutorial regarding transfer ownership). Moving variables to heap space is shown with dot arrow on the pic.<\/p>\n<h2>3.\u00a0\u00a0\u00a0\u00a0 Magic transformation during procedure call<\/h2>\n<p>Let\u2019s consider the following code:<\/p>\n<pre><code class=\"rust\">fn main() {     let args: Vec&lt;String> = env::args().collect();      let ptr: *mut u8 = unsafe { mem::transmute(&amp;args) };     print_mem(ptr, 24 );      let (query, filename) = parse_config(&amp;args); }  fn parse_config(args: &amp;[String]) -> (&amp;str, &amp;str) {     println!(\"args val {:p}\", args);      let query = &amp;args[1];     let filename = &amp;args[2];      (query, filename) }<\/code><\/pre>\n<p>Here we can see declaration of vector \u00a0variable \u201cargs\u201d that takes incoming parameters of main function in vector format.<\/p>\n<pre><code class=\"rust\">let args: Vec&lt;String> = env::args().collect();<\/code><\/pre>\n<p>As result we have local variable \u201cargs\u201d created on stack.<\/p>\n<p>Let\u2019s print it\u2019s content (execute the program with two args: cargo run param1 param2):<\/p>\n<pre><code class=\"rust\">let ptr: *mut u8 = unsafe { mem::transmute(&amp;args) }; print_mem(ptr, 24 );<\/code><\/pre>\n<p>pointer = 0xfa9a4ff730<\/p>\n<p>40 2a 6f b3 6a 02 00 00 03 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00<\/p>\n<p>So as we can see \u201cargs\u201d has three parts: <\/p>\n<p>1)\u00a0\u00a0\u00a0\u00a0\u00a0 Pointer to heap 0x26ab36f2a40 where strings with argument values are allocated<\/p>\n<p>2)\u00a0\u00a0\u00a0\u00a0\u00a0 Capacity of the vector (03)<\/p>\n<p>3)\u00a0\u00a0\u00a0\u00a0\u00a0 Length of the vector (03)<\/p>\n<p>Now let\u2019s have a look at function call:<\/p>\n<pre><code class=\"rust\">let (query, filename) = parse_config(&amp;args);<\/code><\/pre>\n<p>and function definition:<\/p>\n<pre><code class=\"rust\">fn parse_config(args: &amp;[String]) -> (&amp;str, &amp;str) { \u2026 }<\/code><\/pre>\n<p>You can notice some mismatch in calling params type and param definition in function.<\/p>\n<p>The function is defined with <strong><em>&amp;[String])<\/em><\/strong> parameter but is called with address of local variable \u201cargs\u201d that has <strong><em>Vec&lt;String><\/em><\/strong> type.<\/p>\n<p>So let\u2019s try to understand how it can be?<\/p>\n<p>Here some magic of rust compiler happens: as \u201cargs\u201d variable in main function is local variable it\u2019s address itself(address in stack) is not transferred to calling function (actually technically it can be but not done in this way) overwise compiler analyze that expected param of calling function is address of String array <strong><em>&amp;[String]<\/em><\/strong><em>)<\/em> and sends address of start of String array in heap where pointer in \u201cargs\u201d is referring(0x26ab36f2a40 in our example)<\/p>\n<p>We can see it if will print value of \u00a0incoming parameter \u201cargs\u201d in parse_config function:<\/p>\n<pre><code class=\"rust\">println!(\"args val {:p}\", args);<\/code><\/pre>\n<p>output: 0x26ab36f2a40<\/p>\n<p><strong><em>Note: in rust it is called &#171;Implicit Deref Coercions with Functions and Methods&#187;. It is related to Deref trait and you can read about it in more details from official tutorial.<\/em><\/strong><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/657547\/\"> https:\/\/habr.com\/ru\/articles\/657547\/<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<div><!--[--><!--]--><\/div>\n<div id=\"post-content-body\">\n<div>\n<div class=\"article-formatted-body article-formatted-body article-formatted-body_version-2\">\n<div xmlns=\"http:\/\/www.w3.org\/1999\/xhtml\">\n<h2>1. Strings<\/h2>\n<p>In Rust strings can be represented in two ways:<\/p>\n<p>a)\u00a0\u00a0\u00a0\u00a0\u00a0\u00a0 String type <\/p>\n<p>b)\u00a0\u00a0\u00a0\u00a0\u00a0 String slice<\/p>\n<p><strong>String type:<\/strong><\/p>\n<p>String type is defined as a struct of the following structure:<\/p>\n<p>Depending on arch (in my case x86 64bit it is 24byte)<\/p>\n<p><em>{<\/em><\/p>\n<p><em>\u00a0\u00a0 pointer to the address where string characters are stored (8b)<\/em><\/p>\n<p><em>\u00a0\u00a0 capacity (8b)<\/em><\/p>\n<p><em>\u00a0\u00a0 length (8b)<\/em><\/p>\n<p><em>}<\/em><\/p>\n<p><em>\u00a0<\/em>Example:<\/p>\n<pre><code class=\"rust\">let my_string = String::from(\"hello\");<\/code><\/pre>\n<p><strong>String slice (String slice has type &amp;str):<\/strong><\/p>\n<p>There are two types of slices:<\/p>\n<p><strong><em>a)\u00a0\u00a0\u00a0\u00a0\u00a0 built from Strings<\/em><\/strong><\/p>\n<pre><code class=\"rust\">let my_string_slice = &amp;my_string[3..];<\/code><\/pre>\n<p>The goal of such slice is to point on some part of the string<\/p>\n<p>The structure of such slice similar to String:<\/p>\n<p><em>{<\/em><\/p>\n<p><em>\u00a0\u00a0 pointer to the address where string characters are stored (8b)<\/em><\/p>\n<p><em>\u00a0\u00a0 length (8b)<\/em><\/p>\n<p><em>\u00a0\u00a0 starting index (8b)<\/em><\/p>\n<p><em>}<\/em><\/p>\n<p><strong><em>b)\u00a0\u00a0\u00a0\u00a0\u00a0 built from string literals<\/em><\/strong><\/p>\n<pre><code class=\"rust\">let my_str = \"Hello\";<\/code><\/pre>\n<p>It has reduced size:<\/p>\n<p><em>{<\/em><\/p>\n<p><em>\u00a0\u00a0 pointer to the address where string characters are stored (8b)<\/em><\/p>\n<p><em>\u00a0\u00a0 length (8b)<\/em><\/p>\n<p><em>}<\/em><\/p>\n<p><strong>Memory layout:<\/strong><\/p>\n<p>So let\u2019s have a look how does these two types lays on memory:<\/p>\n<p>Let\u2019s consider couple definitions:<\/p>\n<pre><code class=\"rust\">let my_str :&amp;str = \"hello\"; let my_string1: String = my_str.to_string(); let my_string2: String = String::from(\"hello\"); let my_string_slice: &amp;str = &amp;my_string2[1..4];<\/code><\/pre>\n<p>We have defined 4 local variables:<\/p>\n<p>my_str \u2013 is a slice defined from string literals \u201chello\u201d. Such string literals are part of the code(data segment more precisely)<\/p>\n<p>my_string1 \u2013 result of creation String from the slice using to_string method;<\/p>\n<p>my_string2 \u2013 string creation using from method.<\/p>\n<p>my_string_slice \u2013 slice from string<\/p>\n<p>\u00a0The following picture shows memory layout for each variable:<\/p>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>As you can see first variable(&amp;str) is created in stack and pointing to the static location of our code where string literals of \u201chello\u201d are placed<\/p>\n<p>Then when we call to_string() the new variable my_string1 is created and memory on heap is allocated to place \u201chello\u201d chars there.<\/p>\n<p>Same happens when we create my_string2 variable.<\/p>\n<p>Creation of my_string_slice from my_string2 leads to pointing to the same area in heap where my_string2 characters are placed but with shift in address considering starting index<\/p>\n<h2>2.\u00a0\u00a0\u00a0\u00a0 Vectors<\/h2>\n<p>Vectors in rust are dynamically extended data structures;<\/p>\n<p>They have similar structure as String:<\/p>\n<p><em>{<\/em><\/p>\n<p><em>\u00a0\u00a0 pointer to the address in the heap where data are stored (8b)<\/em><\/p>\n<p><em>\u00a0\u00a0 capacity (8b)<\/em><\/p>\n<p><em>\u00a0\u00a0 length (8b)<\/em><\/p>\n<p><em>}<\/em><\/p>\n<p>Let\u2019s have a look what happens when we create new vector and push some data to it (continue of the previous example with strings).<\/p>\n<pre><code class=\"rust\">let mut str_vec = Vec::new(); str_vec.push(my_string1); str_vec.push(my_string2);<\/code><\/pre>\n<figure class=\"full-width\"><figcaption><\/figcaption><\/figure>\n<p>So as we see when we created str_vec variable of type Vec&lt;String> the new memory were allocated in heap. To be more precise during variable creation using new() method pointer of the vector doesn\u2019t point to real heap space. This happen only with first push:<\/p>\n<p>Memory dump:<\/p>\n<pre><code class=\"rust\">let mut str_vec = Vec::new();<\/code><\/pre>\n<p>pointer = 0x3a772ff2f8 (&amp;<u>str_vec<\/u>)<\/p>\n<p>08 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00<\/p>\n<pre><code class=\"rust\">str_vec.push(my_string1);<\/code><\/pre>\n<p>50 5e ca db db 01 00 00 04 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00<\/p>\n<pre><code class=\"rust\">str_vec.push(my_string2);<\/code><\/pre>\n<p>50 5e ca db db 01 00 00 04 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00<\/p>\n<p>So as you can see real address pointing to the heap(0x000001dbdbca5e50) appears only after first push. Initial capacity 4 is reserved at this time as well. When more the 4 elements will be pushed the new area will be allocated with extended size.<\/p>\n<p>\u00a0When <em>my_string1<\/em> and <em>my_string2<\/em> were pushed to the vector two things happened:<\/p>\n<p>1)\u00a0\u00a0\u00a0\u00a0\u00a0 Memory allocation in heap for storing two string variables. Pay attention that pointers of the strings still point to same addresses were chars were stored.<\/p>\n<p>2)\u00a0\u00a0\u00a0\u00a0\u00a0 Move of ownership: <em>my_string<\/em>1 and <em>my_string<\/em>2 are no more accessible as local variables. (see more details from official rust tutorial regarding transfer ownership). Moving variables to heap space is shown with dot arrow on the pic.<\/p>\n<h2>3.\u00a0\u00a0\u00a0\u00a0 Magic transformation during procedure call<\/h2>\n<p>Let\u2019s consider the following code:<\/p>\n<pre><code class=\"rust\">fn main() {     let args: Vec&lt;String> = env::args().collect();      let ptr: *mut u8 = unsafe { mem::transmute(&amp;args) };     print_mem(ptr, 24 );      let (query, filename) = parse_config(&amp;args); }  fn parse_config(args: &amp;[String]) -> (&amp;str, &amp;str) {     println!(\"args val {:p}\", args);      let query = &amp;args[1];     let filename = &amp;args[2];      (query, filename) }<\/code><\/pre>\n<p>Here we can see declaration of vector \u00a0variable \u201cargs\u201d that takes incoming parameters of main function in vector format.<\/p>\n<pre><code class=\"rust\">let args: Vec&lt;String> = env::args().collect();<\/code><\/pre>\n<p>As result we have local variable \u201cargs\u201d created on stack.<\/p>\n<p>Let\u2019s print it\u2019s content (execute the program with two args: cargo run param1 param2):<\/p>\n<pre><code class=\"rust\">let ptr: *mut u8 = unsafe { mem::transmute(&amp;args) }; print_mem(ptr, 24 );<\/code><\/pre>\n<p>pointer = 0xfa9a4ff730<\/p>\n<p>40 2a 6f b3 6a 02 00 00 03 00 00 00 00 00 00 00 03 00 00 00 00 00 00 00<\/p>\n<p>So as we can see \u201cargs\u201d has three parts: <\/p>\n<p>1)\u00a0\u00a0\u00a0\u00a0\u00a0 Pointer to heap 0x26ab36f2a40 where strings with argument values are allocated<\/p>\n<p>2)\u00a0\u00a0\u00a0\u00a0\u00a0 Capacity of the vector (03)<\/p>\n<p>3)\u00a0\u00a0\u00a0\u00a0\u00a0 Length of the vector (03)<\/p>\n<p>Now let\u2019s have a look at function call:<\/p>\n<pre><code class=\"rust\">let (query, filename) = parse_config(&amp;args);<\/code><\/pre>\n<p>and function definition:<\/p>\n<pre><code class=\"rust\">fn parse_config(args: &amp;[String]) -> (&amp;str, &amp;str) { \u2026 }<\/code><\/pre>\n<p>You can notice some mismatch in calling params type and param definition in function.<\/p>\n<p>The function is defined with <strong><em>&amp;[String])<\/em><\/strong> parameter but is called with address of local variable \u201cargs\u201d that has <strong><em>Vec&lt;String><\/em><\/strong> type.<\/p>\n<p>So let\u2019s try to understand how it can be?<\/p>\n<p>Here some magic of rust compiler happens: as \u201cargs\u201d variable in main function is local variable it\u2019s address itself(address in stack) is not transferred to calling function (actually technically it can be but not done in this way) overwise compiler analyze that expected param of calling function is address of String array <strong><em>&amp;[String]<\/em><\/strong><em>)<\/em> and sends address of start of String array in heap where pointer in \u201cargs\u201d is referring(0x26ab36f2a40 in our example)<\/p>\n<p>We can see it if will print value of \u00a0incoming parameter \u201cargs\u201d in parse_config function:<\/p>\n<pre><code class=\"rust\">println!(\"args val {:p}\", args);<\/code><\/pre>\n<p>output: 0x26ab36f2a40<\/p>\n<p><strong><em>Note: in rust it is called &#171;Implicit Deref Coercions with Functions and Methods&#187;. It is related to Deref trait and you can read about it in more details from official tutorial.<\/em><\/strong><\/p>\n<\/div>\n<\/div>\n<\/div>\n<p><!----><!----><\/div>\n<p><!----><!----><br \/> \u0441\u0441\u044b\u043b\u043a\u0430 \u043d\u0430 \u043e\u0440\u0438\u0433\u0438\u043d\u0430\u043b \u0441\u0442\u0430\u0442\u044c\u0438 <a href=\"https:\/\/habr.com\/ru\/articles\/657547\/\"> https:\/\/habr.com\/ru\/articles\/657547\/<\/a><br \/><\/br><\/br><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[],"tags":[],"class_list":["post-404262","post","type-post","status-publish","format-standard","hentry"],"_links":{"self":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/404262","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=404262"}],"version-history":[{"count":0,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=\/wp\/v2\/posts\/404262\/revisions"}],"wp:attachment":[{"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=404262"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=404262"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/savepearlharbor.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=404262"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}