簡體   English   中英

如何在 Rust 中正確設置泛型集合類型

[英]How to set correctly a generic collection type in rust

我正在嘗試在 FreeBSD 12 下測試 Rust 1.39 中的一些語言功能,與 Free Pascal 3.0.4 相比,以使用字符串鍵尋址的 2D 點的簡單通用點集合。 不幸的是,泛型類型聲明的代碼沒有在很早的狀態下編譯並停止:

error[E0106]: missing lifetime specifier
  --> src/main.rs:11:31
   |
11 |     type TPointMap = BTreeMap<&TString, TPoint>;
   |     

我該如何重寫 Rust 代碼?

細節:

為了測試語言行為,我用 Rust 和 Pascal 編寫了兩個小程序,以“語法上”處理相同的上下文。 Pascal 程序是一個簡單的聲明:

  1. 一些普通類型、記錄類型和通用地圖容器,
  2. 這將被用來定義一個點,將這個點存儲在一個新分配的地圖中,
  3. 按鍵搜索點並將數據寫入STDIO
  4. 最后釋放地圖。
program test;
uses fgl; { Use the free pascal generics library }
type
   TDouble   = Double; { Define a 64 bit float } 
   TString   = String; { Define a string type  } 
   TPoint    = record  { Define a point record }
                  X : TDouble; { Coordinate X }
                  Y : TDouble; { Coordinate Y }
               end; 
   { Define a map of points with strings as key }
   TPointMap = specialize TFPGMap<TString, TPoint>;

{ Test program } 
var
   map   : TPointMap; { Declare the map variable }
   point : TPoint;    { Declare a point variable }
   found : TPoint;    { Varaiable for a found point }
   key   : TString;   { Variable to address the point in the map } 
begin
   map := TPointMap.create; { Allocate a new ma container }
   with point do begin      { Set the point variable }
      x := 1.0; y := 2.0;
   end;
   key := '123';              { Set the key address to '123'  }
   map.add(key,point);        { Store the point in the map }

   { Search the point an write the result in the rusty way }
   case map.TryGetData(key, found)  of
     true  : writeln('X: ',found.X:2;, ' Y:', found.Y:2:2);
     false : writeln('Key ''',key,''' not found');
   end;
   map.free;  { De-allocate the map }
   { Plain types are de-allocated by scope }
end.   

該程序編譯並給我:

$ ./main 
X: 1.00 Y:2.00

這是我不正確的 Rust 版本代碼:

use std::collections::BTreeMap; // Use a map from the collection 

type TDouble = f64; // Define the 64 bit float type
type TString = str; // Define the string type
struct TPoint {     // Define the string type
    x: TDouble,     // Coordinate X
    y: TDouble,     // Coordinate Y
}

// Define a map of points with strings as key 
type TPointMap = BTreeMap<&TString, TPoint>;

// Test program
fn main() {
    let point = TPoint { x: 1.0, y: 2.0 }; // Declare and define the point variable
    let mut map = TPointMap::new();        // Declare the map and allocate it
    let key: TString = "123";              // Declare and define the address of point 
    map.insert(&key, point);               // Add the point to the map 
    // search the point and print it
    match map.get(&key) {
        Some(found) => println!("X: {} Y: {}", found.X, found.y),
        None => println!("Key '{}' not found", key),
    }
   // map is de-allocated by scope
}

備注:我知道由於借用和所有權的概念,某些代碼行不能在 Rust 代碼中使用。

 match map.get(&key)...

是其中之一。

為了大致相當於freepascal版本, TString可能應該是String而不是str freepascal String 是( 取決於某些標志)一個指針、一個長度和一個堆分配的字符數組。 這(幾乎)正是String含義。 str只是字符數組並且沒有大小,所以它總是必須在某種(胖)指針后面。

一旦進行了更改,只需要做一些其他事情來修復代碼。 TPointMap需要一個生命周期參數,因為它使用引用類型。 引用的生命周期必須來自某個地方,因此我們在該生命周期中使TPointMap通用的。

type TPointMap<'a> = BTreeMap<&'a TString, TPoint>;

如果您的用例允許,您也可以考慮簡單地使用BTreeMap<TString, TPoint>

我們需要做一些轉換來聲明key: TString 字符串文字的類型為'static str ,但有一個簡單的to_string方法可以將它們轉換為String

let key: TString = "123".to_string(); 

最后, found.X有一個錯字。

Some(found) => println!("X: {} Y: {}", found.x, found.y),

總之,我們有

use std::collections::BTreeMap; // Use a map from the collection

type TDouble = f64; // Define the 64 bit float type
type TString = String; // Define the string type
struct TPoint {
    // Define the string type
    x: TDouble, // Coordinate X
    y: TDouble, // Coordinate Y
}

// Define a map of points with strings as key
type TPointMap<'a> = BTreeMap<&'a TString, TPoint>;

// Test program
fn main() {
    let point = TPoint { x: 1.0, y: 2.0 }; // Declare and define the point variable
    let mut map = TPointMap::new(); // Declare the map and allocate it
    let key: TString = "123".to_string(); // Declare and define the address of point
    map.insert(&key, point); // Add the point to the map
                             // search the point and print it
    match map.get(&key) {
        Some(found) => println!("X: {} Y: {}", found.x, found.y),
        None => println!("Key '{}' not found", key),
    }
    // map is de-allocated by scope
}

(操場)

另請參閱Rust 的Stringstr之間有什么區別?

您看到此失敗的原因是因為您需要一個生命周期作為參考。 Rust 目前無法知道你的引用應該持續多久。

一旦你解決了這個問題,你會遇到這樣一個事實,你不能創建str類型的變量,因為它沒有大小,編譯器無法在編譯時告訴分配多少空間。

您可以在此處進行的最簡單、最少的更改是更改以下幾行:

type TString = &'static str;

type TPointMap = BTreeMap<TString, TPoint>;

(您還需要將 found.X 中的x found.X ,因為 Rust 區分大小寫。)

這將告訴編譯器您的字符串類型是&'static str ,這是字符串文字的類型,因為它們與程序一樣長。

您還可以通過執行以下操作來使其與任何任意生命周期的str引用一起使用:

type TString<'a> = &'a str;

type TPointMap<'a> = BTreeMap<TString<'a>, TPoint>;

但是,在許多程序中,您可能希望插入不限於字符串文字的字符串,並避免在映射的生命周期內進行必要的借用,例如,如果您想從函數返回映射。 在這種情況下,使用擁有的String對象可能是有意義的,以便該對象與地圖一樣長。 在這種情況下,您的代碼如下所示:

use std::collections::BTreeMap; // Use a map from the collection

type TDouble = f64; // Define the 64 bit float type
type TString = String; // Define the string type
struct TPoint {     // Define the string type
    x: TDouble,     // Coordinate X
    y: TDouble,     // Coordinate Y
}

// Define a map of points with strings as key
type TPointMap = BTreeMap<TString, TPoint>;

// Test program
fn main() {
    let point = TPoint { x: 1.0, y: 2.0 }; // Declare and define the point variable
    let mut map = TPointMap::new();        // Declare the map and allocate it
    let key: TString = "123".to_string();  // Declare and define the address of point
    map.insert(key.clone(), point);        // Add the point to the map
    // search the point and print it
    match map.get(&key) {
        Some(found) => println!("X: {} Y: {}", found.x, found.y),
        None => println!("Key '{}' not found", key),
    }
   // map is de-allocated by scope
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM