2017-05-28 44 views
1

我有一個運行Rust程序使用真正的雙打(f64)作爲基礎類型,並希望擴展系統,使它也可以處理複雜的值(num::complex::Complex64)。處理f64或Complex64返回類型。泛型?要麼?

A(削減例如)函數採用一些配置結構config,並根據該輸入的索引idx在產生電位值:

fn potential(config: &Config, idx: &Index3) -> Result<f64, Error> { 
    let num = &config.grid.size; 
    match config.potential { 
     PotentialType::NoPotential => Ok(0.0), 
     PotentialType::Cube => { 
      if (idx.x > num.x/4 && idx.x <= 3 * num.x/4) && 
       (idx.y > num.y/4 && idx.y <= 3 * num.y/4) && 
       (idx.z > num.z/4 && idx.z <= 3 * num.z/4) { 
       Ok(-10.0) 
      } else { 
       Ok(0.0) 
      } 
     } 
     PotentialType::Coulomb => { 
      let r = config.grid.dn * (calculate_r2(idx, &config.grid)).sqrt(); 
      if r < config.grid.dn { 
       Ok(-1./config.grid.dn) 
      } else { 
       Ok(-1./r) 
      } 
     } 
    } 
} 

我現在希望添加ComplexCoulomb匹配它返回一個Complex64值:

PotentialType::ComplexCoulomb => { 
    let r = config.grid.dn * (calculate_r2(idx, &config.grid)).sqrt(); 
    if r < config.grid.dn { 
     Ok(Complex64::new(-1./config.grid.dn, 1.)) 
    } else { 
     Ok(Complex64::new(-1./r, 1.)) 
    } 
} 

此功能在我的程序,它填補了ndarray::Array3早期的切入點;目前我正在使用ndarray::Array3<f64>類型的許多變量 - 所以我需要概括整個程序,而不僅僅是此功能。

如何根據config的輸入擴展此程序以使用這兩種類型?此結構來自解析磁盤上的配置文件,並且會匹配多個PotentialType::Complex*值。

我知道兩種可能的選擇,但我不確定是否符合我的標準。

  1. 使用類似的東西來Either和複雜返回Left真正和Right;然後使用其他邏輯在其他函數中分別處理這些值。
  2. 使用泛型類型。這不是我以前做過的太多事情,generalisation over many types看起來像是我當前代碼庫複雜變化的一大塊。有沒有辦法在這裏降低複雜度?

如果您有任何其他建議,我很樂意聽到他們!

+2

這個問題對於它的「建議」本質來說很棘手(在我看來,它打到了「太寬泛」的邊界)。特別是,您已經列出了兩個選項,但未顯示將它們轉換爲代碼的嘗試。即使我們想要,我們也無法展示「如何降低複雜性」。顯示更多代碼可以改善問題的質量。無論如何,我們已經可以在桌面上放置一些事實:即使函數返回一個「結果」(其中'T'可能是'f64','Complex64',...),我們也不能有運行時字段在'config'中選擇'T'。在最糟糕的情況下,您可能需要2個函數實例。 –

+0

_we在'config'中選擇'T'時不能有運行時字段...您可能需要2個函數實例_ < - 這實際上可能是我的答案。如果其他選項仍然適合,請仔細研究並儘快編輯我的問題。謝謝! – Geodesic

回答

4

可能會有很多代碼更改,但使用泛型參數可能是最靈活的方法,並且不會影響性能。傳遞一個enum的性能會降低,部分原因是枚舉會更大(更大的變體的大小加上一個標籤來區分它們),部分原因是枚舉變體必須經常檢查。

有一件事情可能會變得麻煩,那就是可能會限制你的類型參數的特徵列表。這可以在impl級別完成,而不是在每個功能上完成,以節省重複。目前還沒有一種方法來替代一組特徵,這將使這更符合人體工程學,但有一個RFC批准。

我做了一個非常similar change in the Euclid library。這是一年多以前,從那以後,在Rust和圖書館都發生了很大的變化,但仔細看一下這個提交應該能讓你瞭解必要的變化量。

這是相同的(改名)執行current state

impl <T, Src, Dst> TypedTransform3D<T, Src, Dst> 
where T: Copy + Clone + 
     Add<T, Output=T> + 
     Sub<T, Output=T> + 
     Mul<T, Output=T> + 
     Div<T, Output=T> + 
     Neg<Output=T> + 
     ApproxEq<T> + 
     PartialOrd + 
     Trig + 
     One + Zero { 

    // methods of TypedTransform3D defined here... 

} 

有的這些特質(TrigOneZero)實際上是defined inside the crate,因爲它們不是在標準庫。

+0

謝謝彼得。我會看看你的代碼,並嘗試以類似的方式獲得我的MWE。一旦我確認了一些事情,會將您的答案標記爲正確。 – Geodesic