[英]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 程序是一個簡單的聲明:
STDIO
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 目前無法知道你的引用應該持續多久。
一旦你解決了這個問題,你會遇到這樣一個事實,你不能創建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.