Siz zaten bildiğiniz şeylerle karşılaştırarak öğrenirsiniz. Yakın zamanda Rust'ın geçişli bağımlılık sürüm çözümlemesi konusunda Java gibi çalıştığını varsayarak yanıldım. Bu yazıda, ikisini karşılaştırmak istiyorum.Siz zaten bildiğiniz şeylerle karşılaştırarak öğrenirsiniz. Yakın zamanda Rust'ın geçişli bağımlılık sürüm çözümlemesi konusunda Java gibi çalıştığını varsayarak yanıldım. Bu yazıda, ikisini karşılaştırmak istiyorum.

Rust ve Java'da Geçişli Bağımlılık Sürüm Çözümlemesi: İkisinin Karşılaştırılması

2025/09/21 23:00

透過比較你已知的事物來學習。我最近因為假設 Rust 在處理傳遞性依賴版本解析方面與 Java 相同而吃了虧。在這篇文章中,我想比較這兩者。

依賴、傳遞性和版本解析

在深入探討每個技術棧的細節之前,讓我們先描述這個領域及其相關問題。

\ 當開發任何超過 Hello World 級別的專案時,你很可能會面臨他人曾經遇到過的問題。如果這個問題很普遍,很有可能有人已經善心地將解決方案打包成程式碼,供他人重複使用。現在,你可以使用這個套件並專注於解決你的核心問題。這就是當今產業構建大多數專案的方式,即使它帶來了其他問題:你站在巨人的肩膀上。

\ 程式語言通常附帶可以將這些套件添加到你專案中的構建工具。大多數工具將你添加到專案中的套件稱為依賴。而專案的依賴可能有自己的依賴:後者被稱為傳遞性依賴

Transitive dependencies

在上圖中,C 和 D 是傳遞性依賴。

\ 傳遞性依賴本身存在問題。最大的問題是當一個傳遞性依賴從不同路徑被需要,但版本不同時。在下圖中,A 和 B 都依賴於 C,但依賴於不同版本的 C。

構建工具應該在你的專案中包含哪個版本的 C?Java 和 Rust 有不同的答案。讓我們依次描述它們。

Java 傳遞性依賴版本解析

提醒:Java 程式碼編譯為位元組碼,然後在運行時被解釋(有時也會編譯為本機程式碼,但這超出了我們當前的問題範圍)。我將首先描述運行時依賴解析和構建時依賴解析。

\ 在運行時,Java 虛擬機提供了類路徑的概念。當需要載入一個類時,運行時會按順序搜尋配置的類路徑。想像以下類:

public static Main {     public static void main(String[] args) {         Class.forName("ch.frankel.Dep");     } } 

\ 讓我們編譯並執行它:

java -cp ./foo.jar:./bar.jar Main 

\ 上述命令會首先在 foo.jar 中尋找 ch.frankel.Dep 類。如果找到,它會停止搜尋並載入該類,無論該類是否也存在於 bar.jar 中;如果沒找到,它會進一步在 bar.jar 類中尋找。如果仍未找到,它會失敗並拋出 ClassNotFoundException

\ Java 的運行時依賴解析機制是有序的,並且具有每個類的粒度。無論你是運行 Java 類並在命令行上定義類路徑(如上所示),還是運行在其清單中定義類路徑的 JAR,這都適用。

\ 讓我們將上述程式碼更改為以下內容:

public static Main {     public static void main(String[] args) {         var dep = new ch.frankel.Dep();     } } 

\ 因為新程式碼直接引用 Dep,新程式碼需要在編譯時解析類。類路徑解析的工作方式相同:

javac -cp ./foo.jar:./bar.jar Main 

\ 編譯器會在 foo.jar 中尋找 Dep,如果沒找到,則在 bar.jar 中尋找。這是你在 Java 學習旅程開始時學到的內容。

\ 之後,你的工作單位是 Java 存檔,即 JAR,而不是類。JAR 是一個增強版的 ZIP 存檔,帶有指定其版本的內部清單。

\ 現在,假設你是 foo.jar 的使用者。foo.jar 的開發者在編譯時設置了特定的類路徑,可能包括其他 JAR。你需要這些信息來運行自己的命令。庫開發者如何將這些知識傳遞給下游使用者?

\ 社區提出了一些想法來回答這個問題:第一個被採納的回應是 Maven。Maven 有專案物件模型的概念,你可以在其中設置專案的元數據以及依賴。Maven 可以輕鬆解析傳遞性依賴,因為它們也發布了自己的 POM,包含自己的依賴。因此,Maven 可以追蹤每個依賴的依賴,直到葉子依賴。

\ 現在,回到問題陳述:Maven 如何解決版本衝突?Maven 會為 C 解析哪個依賴版本,1.0 還是 2.0?

\ 文檔很清楚:最近的那個。

Dependency resolution with the same dependency in different versions

在上圖中,到 v1 的路徑距離為二,一個到 B,然後一個到 C;同時,到 v2 的路徑距離為三,一個到 A,然後一個到 D,最後一個到 C。因此,最短路徑指向 v1。

\ 然而,在初始圖中,兩個 C 版本與根工件的距離相同。文檔沒有提供答案。如果你對此感興趣,它取決於 A 和 B 在 POM 中的聲明順序!總之,Maven 返回重複依賴的單一版本,以將其包含在編譯類路徑中。

\ 如果 A 可以使用 C v2.0 或 B 可以使用 C 1.0,那很好!如果不行,你可能需要升級 A 的版本或降級 B 的版本,以便解析的 C 版本可以與兩者兼容。這是一個痛苦的手動過程——問我怎麼知道的。更糟的是,你可能會發現沒有一個 C 版本可以同時與 A 和 B 兼容。是時候替換 A 或 B 了。

Rust 傳遞性依賴版本解析

Rust 在幾個方面與 Java 不同,但我認為以下幾點對於我們的討論最為相關:

  • Rust 在編譯時和運行時具有相同的依賴樹
  • 它提供了一個開箱即用的構建工具,Cargo
  • 依賴是從源碼解析的

\ 讓我們逐一檢視它們。

\ Java 編譯為位元組碼,然後你運行後者。你需要在編譯時和運行時都設置類路徑。使用特定類路徑編譯並使用不同的類路徑運行可能導致錯誤。例如,想像你使用你依賴的類進行編譯,但該類在運行時不存在。或者,它存在,但版本不兼容。

\ 與這種模塊化方法相反,Rust 將 crate 的程式碼和每個依賴編譯為一個獨特的本機包。此外,Rust 也提供了自己的構建工具,從而避免了記住不同工具怪癖的需要。我提到了 Maven,但其他構建工具在上述用例中可能有不同的版本解析規則。

\ 最後,Java 從二進制文件解析依賴:JAR。相反,Rust 從源碼解析依賴。在構建時,Cargo 解析整個依賴樹,下載所有需要的源碼,並按正確的順序編譯它們。

\ 考慮到這一點,Rust 如何解決初始問題中的 C 依賴版本?如果你來自 Java 背景,答案可能看起來很奇怪,但Rust 兩者都包含。實際上,在上圖中,Rust 將使用 C v1.0 編譯 A,並使用 C v2.0 編譯 B。問題解決了。

結論

JVM 語言,特別是 Java,提供了編譯時類路徑和運行時類路徑。它允許模塊化和重用性,但也為類路徑解析問題打開了大門。另一方面,Rust 將你的 crate 構建為一個獨立的二進制文件,無論是庫還是可執行文件。

\ 進一步了解:

  • Maven - 依賴機制介紹
  • 高效 Rust - 第 25 條:管理你的依賴圖

最初發表於 A Java Geek,2025 年 9 月 14 日

Sorumluluk Reddi: Bu sitede yeniden yayınlanan makaleler, halka açık platformlardan alınmıştır ve yalnızca bilgilendirme amaçlıdır. MEXC'nin görüşlerini yansıtmayabilir. Tüm hakları telif sahiplerine aittir. Herhangi bir içeriğin üçüncü taraf haklarını ihlal ettiğini düşünüyorsanız, kaldırılması için lütfen service@support.mexc.com ile iletişime geçin. MEXC, içeriğin doğruluğu, eksiksizliği veya güncelliği konusunda hiçbir garanti vermez ve sağlanan bilgilere dayalı olarak alınan herhangi bir eylemden sorumlu değildir. İçerik, finansal, yasal veya diğer profesyonel tavsiye niteliğinde değildir ve MEXC tarafından bir tavsiye veya onay olarak değerlendirilmemelidir.